Skip to content

Reverse Proxy

Running ArchVault behind a reverse proxy is required for production deployments. A reverse proxy handles TLS termination (HTTPS), so traffic between clients and your server is encrypted. Without one, credentials and session tokens are sent in plain text.

  • ArchVault running via Docker Compose on port 3000
  • A domain name pointing to your server (e.g. archvault.example.com)
  • Ports 80 and 443 open on your firewall
Client → (HTTPS :443) → Reverse Proxy → (HTTP :3000) → ArchVault container

The reverse proxy terminates TLS and forwards requests to ArchVault over the internal Docker network. Make sure BETTER_AUTH_URL in your .env matches the public URL (e.g. https://archvault.example.com).


Traefik is a cloud-native reverse proxy that integrates with Docker via label-based configuration. It automatically discovers services and provisions TLS certificates.

The production Compose file includes a Traefik service as an optional profile. This is the fastest way to get HTTPS running — no config files to create or copy.

  1. Set DOMAIN and ACME_EMAIL in your .env:

    .env
    DOMAIN=archvault.example.com
    ACME_EMAIL=admin@example.com
  2. Start the stack with the traefik profile:

    Terminal window
    docker compose -f compose.prod.yaml --profile traefik up -d

That’s it. Traefik automatically provisions a TLS certificate via Let’s Encrypt, redirects HTTP to HTTPS, and proxies to ArchVault. Certificate data is persisted in the letsencrypt Docker volume so renewals survive restarts.

If you prefer to configure Traefik yourself rather than using the built-in profile:

compose.prod.yaml
services:
traefik:
image: traefik:v3
restart: unless-stopped
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.network=archvault"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--certificatesresolvers.letsencrypt.acme.email=admin@example.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- letsencrypt:/letsencrypt
security_opt:
- no-new-privileges:true
networks:
- archvault
app:
# ... existing app config ...
ports: [] # Remove host port binding — Traefik handles it
labels:
- "traefik.enable=true"
- "traefik.http.routers.archvault.rule=Host(`archvault.example.com`)"
- "traefik.http.routers.archvault.entrypoints=websecure"
- "traefik.http.routers.archvault.tls.certresolver=letsencrypt"
- "traefik.http.services.archvault.loadbalancer.server.port=3000"
# Security headers
- "traefik.http.middlewares.archvault-headers.headers.stsSeconds=31536000"
- "traefik.http.middlewares.archvault-headers.headers.stsIncludeSubdomains=true"
- "traefik.http.middlewares.archvault-headers.headers.stsPreload=true"
- "traefik.http.middlewares.archvault-headers.headers.frameDeny=true"
- "traefik.http.middlewares.archvault-headers.headers.contentTypeNosniff=true"
- "traefik.http.middlewares.archvault-headers.headers.referrerPolicy=strict-origin-when-cross-origin"
- "traefik.http.routers.archvault.middlewares=archvault-headers"
networks:
- archvault
volumes:
letsencrypt:
LabelPurpose
traefik.enable=trueOpt this service into Traefik discovery
traefik.http.routers.archvault.ruleMatch requests by hostname
traefik.http.routers.archvault.tls.certresolverUse Let’s Encrypt for TLS
traefik.http.services.archvault.loadbalancer.server.portForward to port 3000

To enable the Traefik dashboard for monitoring, add:

command:
# ... existing commands ...
- "--api.dashboard=true"
- "--api.insecure=false"
labels:
- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"

After configuring your reverse proxy, verify everything works:

Terminal window
# Check HTTPS is working
curl -I https://archvault.example.com
# Check health endpoint through the proxy
curl https://archvault.example.com/api/health
# Verify TLS certificate
openssl s_client -connect archvault.example.com:443 -servername archvault.example.com < /dev/null 2>/dev/null | openssl x509 -noout -dates

You should see:

  • HTTP 200 from the health endpoint
  • A valid TLS certificate
  • Strict-Transport-Security in the response headers
IssueSolution
502 Bad GatewayArchVault container isn’t running or isn’t on the same Docker network
Certificate errorsEnsure your domain’s DNS A record points to the server’s public IP
Mixed content warningsSet BETTER_AUTH_URL to your https:// URL in .env
WebSocket errorsEnsure Upgrade and Connection headers are forwarded (see Nginx config)