Production Deployment Guide — HenKaiPan ASPM

This guide covers deploying HenKaiPan ASPM in production environments with Docker Compose. For Kubernetes deployments, see Kubernetes Deployment Guide.

Architecture Overview

                         ┌──────────────┐
                         │   Reverse     │
                         │   Proxy       │
                         │  (nginx/      │
                         │   caddy)      │
                         └──┬─────────┬──┘
                            │         │
                      :443  │   :9090 │
                      HTTPS │   metrics
                     ┌──────▼──┐ ┌────▼─────┐
                     │  API    │ │ Prometheus│
                     │ :8080   │ │ (optional)│
                     └──┬───┬──┘ └──────────┘
                        │   │
                  ┌─────▼───▼──────┐
                  │  PostgreSQL    │
                  │  + Redis       │
                  └────────────────┘

                    ┌──────────────┐
                    │   Worker     │
                    │  (embedded   │
                    │   scanners)  │
                    └──────────────┘
  • API (ghcr.io/dyallab/henkaipan-api): Serves the frontend and REST API on port 8080. Exposes Prometheus metrics on port 9090.
  • Worker (ghcr.io/dyallab/henkaipan-worker): Executes background jobs (scans, webhooks, emails). Scanner binaries are bundled in the image.
  • PostgreSQL (port 5432): Primary data store.
  • Redis (port 6379): Job queue, rate-limiting, and SSE event relay between worker and API processes.

Prerequisites

  • Docker & Docker Compose v2.24+
  • Linux host (recommended) or Windows with WSL2
  • 8 GB RAM minimum (16 GB recommended for concurrent scans)
  • 30 GB free disk (scanner images ~6 GB, plus data)
  • Domain name with DNS pointing to your server
  • Ports 80 and 443 open (for reverse proxy)

1. Initial Setup

# Clone the repository
git clone https://github.com/Dyallab/HenKaiPan-self-hosted.git
cd HenKaiPan-self-hosted

# Run the installer (checks prerequisites, generates secrets)
./install.sh

# To skip Ollama installation (e.g. if you use another AI provider):
# ./install.sh --skip-ollama

# Start the stack
docker compose up -d

# Verify
curl http://localhost:8080/api/health

The API serves both the backend API and the embedded frontend on port 8080. Open http://your-server:8080 and log in with the admin credentials generated by install.sh.

Ollama: By default, the installer sets up Ollama (free, self-hosted AI) for summary generation. Use ./install.sh --skip-ollama if you don't need it or plan to use a different AI provider.

2. Reverse Proxy with TLS

Option A: Nginx + Let's Encrypt (Recommended)

Create /etc/nginx/sites-available/henkaipan:

server {
    listen 80;
    server_name aspm.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name aspm.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/aspm.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/aspm.yourdomain.com/privkey.pem;

    # Security headers
    add_header Strict-Transport-Security "max-age=63072000" always;
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options DENY;
    # API and frontend
    location / {
        proxy_pass http://127.0.0.1:8080;
        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;
    }
}

Enable and obtain TLS certificate:

sudo ln -s /etc/nginx/sites-available/henkaipan /etc/nginx/sites-enabled/
sudo certbot --nginx -d aspm.yourdomain.com
sudo systemctl reload nginx

Option B: Caddy (Simpler, Auto-TLS)

Create /etc/caddy/Caddyfile:

aspm.yourdomain.com {
    reverse_proxy 127.0.0.1:8080

    headers {
        Strict-Transport-Security "max-age=63072000"
    }
}

Caddy automatically obtains and renews TLS certificates.

3. Environment Configuration

After running install.sh, review and adjust your .env file:

Required Security Settings

# Must be set behind HTTPS
COOKIE_SECURE=true

# Generate strong random values
JWT_SECRET=<openssl rand -base64 32>
SECRET_ENCRYPTION_KEY=<openssl rand -hex 32>

# Database (defaults are fine for single-host)
DATABASE_URL=postgres://aspm:aspm@postgres:5432/aspm?sslmode=disable

Performance Tuning (optional)

# Increase API server timeouts for large scan payloads
# (configurable via Go server, defaults are reasonable)

# Redis for rate limiting / queue
REDIS_ADDR=redis:6379

4. Security Hardening

Scanner Execution

Scanner binaries run directly within the worker process. All scanners are bundled in the worker Docker image, eliminating the need for Docker socket access.

Network Security

# Block external access to PostgreSQL and Redis
# These should only be accessible from the Docker network
docker compose port postgres 5432  # Should NOT be exposed publicly

If you do not need external DB access, remove ports from postgres and redis in docker-compose.yml. They will remain accessible within the Docker network.

Secrets Management

  • The .env file contains sensitive credentials. Set COOKIE_SECURE=true so auth cookies are only sent over HTTPS.
  • Restrict .env file permissions: chmod 600 .env
  • Consider using Docker Secrets or a secret manager (Hashicorp Vault, AWS Secrets Manager) for production.

Firewall Rules

# Allow only SSH, HTTP, and HTTPS
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw deny 8080  # API should only be accessed via reverse proxy
sudo ufw enable

5. Database Backups

Automated Backup Script

The landing page provides a Backup & Restore guide with a backup script. Set up automated daily backups:

# Install backup script
mkdir -p /opt/henkaipan/backups
cp scripts/backup.sh /opt/henkaipan/backups/

# Add cron job (runs daily at 2 AM)
echo "0 2 * * * /opt/henkaipan/backups/backup.sh >> /var/log/henkaipan-backup.log 2>&1" | crontab -

Restore Procedure

# Stop the API to prevent writes
docker compose stop api worker

# Restore from backup
docker compose exec -T postgres psql -U aspm -d aspm < /path/to/backup.sql

# Restart
docker compose up -d

6. Monitoring

Prometheus Metrics

The API exposes metrics on port 9090 (configurable via PROMETHEUS_PORT):

# Quick check
curl http://localhost:9090/metrics

A sample Prometheus configuration is available at monitoring/prometheus.yml. Key metrics include:

  • Queue depth (Asynq)
  • Request latency and rate
  • Database connection pool stats
  • Go runtime metrics

Health Checks

# Full health check (DB, Redis, Worker status)
curl http://localhost:8080/api/health

# Version info
curl http://localhost:8080/api/version

Logging

# View API logs
docker compose logs -f --tail=100 api

# View Worker logs
docker compose logs -f --tail=100 worker

# Limit log size in docker-compose.yml
x-logging: &logging
  logging:
    options:
      max-size: "10m"
      max-file: "3"

7. Upgrading

Standard Upgrade

# Pull new images
docker compose pull

# Recreate containers with new images
docker compose up -d

# Verify health
curl http://localhost:8080/api/health

Migrations run automatically on API startup. Check API logs for migration output:

docker compose logs api | grep -i migration

Pinning to a Specific Version

To control exactly which version runs, override the image tag in docker-compose.yml:

services:
  api:
    image: ghcr.io/dyallab/henkaipan-api:v1.0.0
  worker:
    image: ghcr.io/dyallab/henkaipan-worker:v1.0.0

Rollback

If an upgrade causes issues:

  1. Revert image tags to the previous version in docker-compose.yml
  2. Recreate containers:
docker compose up -d
  1. Verify rollback:
curl http://localhost:8080/api/version
curl http://localhost:8080/api/health

Database note: Migrations are additive (new columns, new tables). Downgrading the application code does not automatically revert schema changes. This is safe — the application remains compatible with the newer schema. If you need a full rollback including database schema, restore from a backup taken before the upgrade.

Pre-Upgrade Checklist

  • Review the CHANGELOG.md for breaking changes
  • Take a database backup
  • Note current version: curl http://localhost:8080/api/version
  • Test in a staging environment if possible

8. Production Checklist

Before going live:

Security

  • COOKIE_SECURE=true set in .env
  • COOKIE_DOMAIN=.example.com set for your domain
  • COOKIE_SAMESITE=lax (default) or strict for stricter CSRF protection
  • CORS_ALLOWED_ORIGINS=https://aspm.example.com set (comma-separated for multiple origins)
  • TRUSTED_PROXIES set if behind a reverse proxy (e.g. 10.0.0.0/8)
  • PUBLIC_API_BASE= (empty) so API and frontend are served from same origin
  • Reverse proxy (nginx/caddy) with valid TLS certificate
  • Firewall restricts direct access to API port 8080
  • PostgreSQL and Redis ports not exposed to the internet
  • Strong random JWT_SECRET and SECRET_ENCRYPTION_KEY generated
  • .env file permissions: chmod 600 .env
  • Default credentials rotated (ADMIN_USER, ADMIN_PASS)
  • SSO configured with TLS-terminated SSO_REDIRECT_URI and matching IdP redirect URI (see SSO with Authelia)

Reliability

  • Database backups configured (daily + offsite)
  • Backup restore procedure tested
  • Monitoring set up (health endpoint, Prometheus metrics)
  • Log aggregation configured (log rotation, external sink if needed)
  • Resource limits set in docker-compose.yml (optional but recommended)

Maintenance

  • Upgrade procedure documented and tested
  • Rollback plan documented
  • DNS records configured (A/AAAA record for your domain)
  • Email notifications configured (SMTP) for alerts

9. Resource Sizing

Deployment Size Users RAM Disk Workers
Small team 1-10 8 GB 30 GB 1
Medium team 10-50 16 GB 100 GB 2
Large team 50-200 32 GB 250 GB 3-4

Disk usage is dominated by Docker images (scanners). Actual database storage is minimal for most deployments (< 1 GB for thousands of findings).

10. Related Documentation