hatch-surf/docs/ARCHITECTURE.md
Chris Anderson 9b07214cb8 feat: Dockerized deployment and home cleanup
- Replace rsync deployment with Docker-based deployment
- Container runs nginx on port 3000, proxied by host nginx
- Add architecture documentation
- Add home directory cleanup script
- No more direct /var/www writes

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-06-24 09:43:10 +02:00

4.0 KiB

Hatch.surf Architecture

Overview

Hatch.surf is the landing page and brand site for El Foundation. It consists of:

  • Static frontend (site/) — HTML/CSS/JS served via nginx
  • Go backend (cmd/hatch) — API server for future dynamic features

Deployment Architecture

Current State (Deprecated)

GitHub Push → GitHub Actions → rsync → /var/www/hatch.surf → nginx (host)

Problems:

  • Direct filesystem deployment — no isolation
  • Host-dependent — can't reproduce locally
  • Secrets (SSH keys) required for rsync

New Architecture

GitHub Push → GitHub Actions → Build Docker Image → SCP to Server → Docker Run → nginx reverse proxy

Benefits:

  • Containerized — isolated from host
  • Reproducible — same image in dev/prod
  • No host filesystem dependency
  • Easy rollback — just change image tag

Docker Setup

Frontend (site/)

Dockerfile:

FROM nginx:alpine
RUN rm -rf /usr/share/nginx/html/*
COPY . /usr/share/nginx/html/
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

docker-compose.yml:

services:
  hatch-homepage:
    build: .
    ports:
      - "127.0.0.1:3000:80"
    volumes:
      - .:/usr/share/nginx/html:ro
    restart: unless-stopped

Backend (cmd/hatch)

Dockerfile:

FROM golang:1.25-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . ./
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /bin/hatch ./cmd/hatch

FROM scratch
COPY --from=build /bin/hatch /bin/hatch
EXPOSE 8080
ENTRYPOINT ["/bin/hatch"]

docker-compose.yml:

services:
  hatch:
    build: .
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:8080"
    volumes:
      - hatch-data:/data
    environment:
      - HATCH_PORT=8080
      - HATCH_DB_PATH=/data/hatch.db
      - HATCH_BASE_URL=${HATCH_BASE_URL:-http://localhost}

  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy-data:/data
      - caddy-config:/config
    environment:
      - HATCH_HOSTNAME=${HATCH_HOSTNAME:-localhost}
    profiles:
      - with-caddy

volumes:
  hatch-data:
  caddy-data:
  caddy-config:

Deployment Flow

GitHub Actions (deploy-site.yml)

  1. Builddocker build -t hatch-homepage:latest ./site
  2. Savedocker save hatch-homepage:latest | gzip > hatch-homepage.tar.gz
  3. SCP — Copy tarball to server
  4. Loaddocker load < /tmp/hatch-homepage.tar.gz
  5. Rundocker run -d --name hatch-homepage ...

Server Configuration

Nginx reverse proxy (host nginx):

server {
    listen 80;
    server_name hatch.surf;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name hatch.surf;

    ssl_certificate /etc/letsencrypt/live/hatch.surf/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/hatch.surf/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Secrets Management

  • DEPLOY_HOST — Server IP/hostname
  • DEPLOY_USER — SSH user (nara)
  • DEPLOY_KEY — SSH private key (stored in GitHub Secrets)

No secrets in repo. All deployment credentials in GitHub Secrets.

Rollback Procedure

If deployment fails:

# On server
docker stop hatch-homepage
docker rm hatch-homepage
docker run -d --name hatch-homepage --restart unless-stopped -p 127.0.0.1:3000:80 hatch-homepage:previous-tag

Future Improvements

  1. Container registry — Push images to GitHub Container Registry (ghcr.io)
  2. Health checks — Add HEALTHCHECK to Dockerfile
  3. Monitoring — Add Prometheus metrics endpoint
  4. CDN — Serve static assets via Cloudflare/CloudFront
  5. SSL automation — Certbot in container or Caddy auto-SSL