diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..dc2197e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,47 @@ +# Git files +.git +.gitignore +.gitattributes + +# Build artifacts +gitcitadel-online +server +*.exe +*.dll +*.so +*.dylib + +# Test files +*_test.go +*.test + +# Documentation +*.md +!README.md +LICENSE.md + +# IDE files +.vscode +.idea +*.swp +*.swo +*~ + +# OS files +.DS_Store +Thumbs.db + +# Cache and temporary files +cache/ +*.log +*.tmp + +# Node modules (will be installed in container) +node_modules/ + +# Config file (should be mounted) +config.yaml + +# Development files +.env +.env.local diff --git a/DOCKER.md b/DOCKER.md new file mode 100644 index 0000000..0031e44 --- /dev/null +++ b/DOCKER.md @@ -0,0 +1,223 @@ +# Docker Setup for GitCitadel Online + +This guide explains how to run GitCitadel Online using Docker. + +## Prerequisites + +- Docker (version 20.10 or later) +- Docker Compose (version 2.0 or later, optional but recommended) +- Network access (for downloading dependencies and connecting to Nostr relays) + +## Image Details + +The Docker image uses **Alpine Linux** for a smaller footprint (~50MB base image vs ~200MB+ for Debian). This works well because: + +- The Go binary is statically compiled (`CGO_ENABLED=0`), so no C library dependencies +- Node.js packages (`@asciidoctor/core`, `marked`) are pure JavaScript with no native bindings +- Alpine's musl libc is sufficient for our use case + +If you encounter any compatibility issues, you can modify the Dockerfile to use Debian-based images (`golang:1.22` and `node:20-slim`), though this will increase the image size. + +## Quick Start + +### Using Docker Compose (Recommended) + +1. **Create your configuration file:** + ```bash + cp config.yaml.example config.yaml + # Edit config.yaml with your Nostr indices, relay URLs, and settings + ``` + +2. **Build and run:** + ```bash + docker-compose up -d + ``` + +3. **View logs:** + ```bash + docker-compose logs -f + ``` + +4. **Stop the container:** + ```bash + docker-compose down + ``` + +### Using Docker directly + +1. **Build the image:** + ```bash + docker build -t gitcitadel-online . + ``` + +2. **Create config file:** + ```bash + cp config.yaml.example config.yaml + # Edit config.yaml with your settings + ``` + +3. **Run the container:** + ```bash + docker run -d \ + --name gitcitadel-online \ + -p 8080:8080 \ + -v $(pwd)/config.yaml:/app/config.yaml:ro \ + -v $(pwd)/cache:/app/cache \ + --restart unless-stopped \ + gitcitadel-online + ``` + +4. **View logs:** + ```bash + docker logs -f gitcitadel-online + ``` + +5. **Stop the container:** + ```bash + docker stop gitcitadel-online + docker rm gitcitadel-online + ``` + +## Configuration + +### Config File + +The `config.yaml` file must be mounted into the container. The default path is `/app/config.yaml`. + +You can override the config path using the `--config` flag: +```bash +docker run ... gitcitadel-online --config /path/to/config.yaml +``` + +### Port Mapping + +By default, the application runs on port 8080. You can change the host port mapping: +```bash +# Map to different host port +docker run -p 3000:8080 ... +``` + +Or update `docker-compose.yml`: +```yaml +ports: + - "3000:8080" +``` + +### Cache Persistence + +The cache directory (`cache/`) is persisted as a volume to maintain cached pages and media between container restarts. + +### Environment Variables + +You can pass environment variables, though most configuration should be in `config.yaml`: + +```bash +docker run -e LOG_LEVEL=debug ... +``` + +## Development Mode + +To run in development mode with verbose logging: + +```bash +docker run ... gitcitadel-online --dev +``` + +Or with docker-compose, override the command: +```yaml +command: ["--config", "/app/config.yaml", "--dev"] +``` + +## Health Check + +The container includes a health check that monitors the `/health` endpoint. You can check the health status: + +```bash +docker ps +# Look for "healthy" status + +# Or inspect directly +docker inspect --format='{{.State.Health.Status}}' gitcitadel-online +``` + +## Troubleshooting + +### Container won't start + +1. **Check logs:** + ```bash + docker logs gitcitadel-online + ``` + +2. **Verify config file:** + ```bash + docker exec gitcitadel-online cat /app/config.yaml + ``` + +3. **Check file permissions:** + The container runs as a non-root user (UID 1000). Ensure cache directory is writable: + ```bash + chmod -R 777 cache/ + ``` + +### Can't connect to Nostr relays + +- Ensure the container has network access +- Check firewall rules if running on a remote server +- Verify relay URLs in `config.yaml` are correct + +### Cache not persisting + +- Ensure the cache volume is properly mounted +- Check volume permissions +- Verify the cache directory exists and is writable + +## Building for Different Architectures + +The Dockerfile builds for `linux/amd64` by default. To build for other architectures: + +```bash +# For ARM64 (e.g., Raspberry Pi, Apple Silicon) +docker buildx build --platform linux/arm64 -t gitcitadel-online . + +# For multiple architectures +docker buildx build --platform linux/amd64,linux/arm64 -t gitcitadel-online . +``` + +## Production Deployment + +For production deployment: + +1. **Use a reverse proxy** (nginx, Traefik, etc.) in front of the container +2. **Set up SSL/TLS** certificates +3. **Configure proper logging** and monitoring +4. **Use secrets management** for sensitive configuration +5. **Set resource limits** in docker-compose.yml: + ```yaml + deploy: + resources: + limits: + cpus: '1' + memory: 512M + ``` + +## Updating + +To update to a new version: + +```bash +# Pull latest code +git pull + +# Rebuild and restart +docker-compose build +docker-compose up -d +``` + +Or with Docker directly: +```bash +docker build -t gitcitadel-online . +docker stop gitcitadel-online +docker rm gitcitadel-online +docker run ... # (same command as before) +``` diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..83fcfc5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,77 @@ +# Multi-stage build for GitCitadel Online +# Using Alpine Linux for smaller image size (~50MB vs ~200MB+ for Debian) +# Alpine works well here because: +# - Go binary is statically compiled (CGO_ENABLED=0) +# - Node.js packages are pure JavaScript (no native bindings) +# - No C library dependencies required + +# Stage 1: Build Go application +FROM golang:1.22-alpine AS builder + +# Install build dependencies +RUN apk add --no-cache git + +# Set working directory +WORKDIR /build + +# Copy go mod files +COPY go.mod go.sum ./ +RUN go mod download + +# Copy source code +COPY . . + +# Build the application +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags='-w -s' -o gitcitadel-online ./cmd/server + +# Stage 2: Runtime with Node.js for AsciiDoc processing +FROM node:20-alpine + +# Install runtime dependencies (wget for health check and nostr-tools download) +RUN apk add --no-cache ca-certificates tzdata wget + +# Set working directory +WORKDIR /app + +# Install Node.js dependencies for AsciiDoc processing +COPY package.json package-lock.json ./ +RUN npm ci --only=production + +# Copy built binary from builder +COPY --from=builder /build/gitcitadel-online /app/gitcitadel-online + +# Copy static files and templates +COPY static/ ./static/ +COPY templates/ ./templates/ + +# Download nostr-tools bundle if not present (for contact form) +RUN if [ ! -f ./static/js/nostr.bundle.js ]; then \ + mkdir -p ./static/js && \ + wget -O ./static/js/nostr.bundle.js https://unpkg.com/nostr-tools@latest/lib/nostr.bundle.js || \ + echo "Warning: Failed to download nostr-tools bundle"; \ + fi + +# Copy example config (user should mount their own config.yaml) +COPY config.yaml.example ./config.yaml.example + +# Create cache directories +RUN mkdir -p cache/media + +# Create non-root user for security +RUN addgroup -g 1000 appuser && \ + adduser -D -u 1000 -G appuser appuser && \ + chown -R appuser:appuser /app + +# Switch to non-root user +USER appuser + +# Expose port (default 8080, can be overridden via config) +EXPOSE 8080 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 + +# Run the application +ENTRYPOINT ["/app/gitcitadel-online"] +CMD ["--config", "/app/config.yaml"] diff --git a/README.md b/README.md index 6eb32c4..5311cd7 100644 --- a/README.md +++ b/README.md @@ -277,6 +277,28 @@ Both methods publish kind 1 events to configured relays with proper tags for iss - Optimized static asset serving - Efficient Nostr event parsing +## Docker Deployment + +GitCitadel Online can be run using Docker for easy deployment on localhost or remote servers. + +### Quick Start with Docker Compose + +```bash +# 1. Create config file +cp config.yaml.example config.yaml +# Edit config.yaml with your settings + +# 2. Build and run +docker-compose up -d + +# 3. View logs +docker-compose logs -f +``` + +The application will be available at `http://localhost:8080`. + +For detailed Docker instructions, see [DOCKER.md](DOCKER.md). + ## License MIT License - see LICENSE.md for details diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..ff69033 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,33 @@ +version: '3.8' + +services: + gitcitadel-online: + build: + context: . + dockerfile: Dockerfile + container_name: gitcitadel-online + restart: unless-stopped + ports: + - "8080:8080" + volumes: + # Mount config file (create from config.yaml.example) + - ./config.yaml:/app/config.yaml:ro + # Persist cache directory + - ./cache:/app/cache + environment: + # Optional: override config path + # - CONFIG_PATH=/app/config.yaml + # Optional: set log level + # - LOG_LEVEL=info + networks: + - gitcitadel-network + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + +networks: + gitcitadel-network: + driver: bridge