b9a88c4330
Caddy's certmagic creates storage dirs with hardcoded 0700 permissions, making the web container's supplementary group membership ineffective. Rather than working around this with ACLs or chmod hacks, remove the feature entirely — it was cosmetic (issuer/expiry display) for certs that Caddy auto-manages anyway. Also bump access list dropdown timeout from 5s to 10s to fix flaky E2E test. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
187 lines
6.1 KiB
YAML
187 lines
6.1 KiB
YAML
services:
|
|
web:
|
|
container_name: caddy-proxy-manager-web
|
|
image: ghcr.io/fuomag9/caddy-proxy-manager-web:latest
|
|
build:
|
|
context: .
|
|
dockerfile: docker/web/Dockerfile
|
|
args:
|
|
# User and group IDs for rootless operation
|
|
# Set these to match your host user to avoid permission issues
|
|
# Find your UID/GID with: id -u / id -g
|
|
PUID: ${PUID:-10001}
|
|
PGID: ${PGID:-10001}
|
|
restart: unless-stopped
|
|
ports:
|
|
- "3000:3000"
|
|
environment:
|
|
# Node environment
|
|
NODE_ENV: production
|
|
|
|
# REQUIRED: Session secret for encrypting cookies and sessions
|
|
# Generate with: openssl rand -base64 32
|
|
# SECURITY: You MUST set this to a unique value in production!
|
|
SESSION_SECRET: ${SESSION_SECRET:?ERROR - SESSION_SECRET is required}
|
|
|
|
# Caddy API endpoint (internal communication)
|
|
CADDY_API_URL: ${CADDY_API_URL:-http://caddy:2019}
|
|
|
|
# Public base URL for the application
|
|
BASE_URL: ${BASE_URL:-http://localhost:3000}
|
|
|
|
# Database configuration
|
|
DATABASE_PATH: /app/data/caddy-proxy-manager.db
|
|
DATABASE_URL: file:/app/data/caddy-proxy-manager.db
|
|
|
|
# NextAuth configuration
|
|
NEXTAUTH_URL: ${BASE_URL:-http://localhost:3000}
|
|
|
|
# REQUIRED: Admin credentials for login
|
|
# SECURITY: You MUST set these to secure values in production!
|
|
# Password must be 12+ chars with uppercase, lowercase, numbers, and special chars
|
|
ADMIN_USERNAME: ${ADMIN_USERNAME:?ERROR - ADMIN_USERNAME is required}
|
|
ADMIN_PASSWORD: ${ADMIN_PASSWORD:?ERROR - ADMIN_PASSWORD is required}
|
|
|
|
# OAuth2/OIDC Authentication (Optional - works with Authentik, Authelia, Keycloak, etc.)
|
|
OAUTH_ENABLED: ${OAUTH_ENABLED:-false}
|
|
OAUTH_PROVIDER_NAME: ${OAUTH_PROVIDER_NAME:-OAuth2}
|
|
OAUTH_CLIENT_ID: ${OAUTH_CLIENT_ID:-}
|
|
OAUTH_CLIENT_SECRET: ${OAUTH_CLIENT_SECRET:-}
|
|
OAUTH_ISSUER: ${OAUTH_ISSUER:-}
|
|
OAUTH_AUTHORIZATION_URL: ${OAUTH_AUTHORIZATION_URL:-}
|
|
OAUTH_TOKEN_URL: ${OAUTH_TOKEN_URL:-}
|
|
OAUTH_USERINFO_URL: ${OAUTH_USERINFO_URL:-}
|
|
OAUTH_ALLOW_AUTO_LINKING: ${OAUTH_ALLOW_AUTO_LINKING:-false}
|
|
group_add:
|
|
- "${CADDY_GID:-10000}" # caddy's GID — lets the web user read /logs/access.log
|
|
volumes:
|
|
- caddy-manager-data:/app/data
|
|
- geoip-data:/usr/share/GeoIP:ro,z
|
|
- caddy-logs:/logs:ro
|
|
depends_on:
|
|
caddy:
|
|
condition: service_healthy
|
|
networks:
|
|
- caddy-network
|
|
healthcheck:
|
|
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/api/health',r=>{process.exit(r.statusCode<400?0:1)}).on('error',()=>process.exit(1))"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 40s
|
|
|
|
caddy:
|
|
container_name: caddy-proxy-manager-caddy
|
|
image: ghcr.io/fuomag9/caddy-proxy-manager-caddy:latest
|
|
build:
|
|
context: .
|
|
dockerfile: docker/caddy/Dockerfile
|
|
args:
|
|
# User and group IDs for rootless operation
|
|
# Set these to match your host user to avoid permission issues
|
|
# Find your UID/GID with: id -u / id -g
|
|
PUID: ${PUID:-10000}
|
|
PGID: ${PGID:-10000}
|
|
restart: unless-stopped
|
|
ports:
|
|
- "80:80"
|
|
- "80:80/udp"
|
|
- "443:443"
|
|
- "443:443/udp"
|
|
# - "2019:2019" # Admin API
|
|
# Admin API (port 2019) is only exposed on internal network for security, enable at your risk
|
|
# Web UI accesses via http://caddy:2019 internally
|
|
# Uncomment the line below to expose metrics externally for Grafana/Prometheus
|
|
# - "9090:9090" # Metrics available at http://localhost:9090/metrics (configure in Settings first)
|
|
environment:
|
|
# Primary domain for Caddy configuration
|
|
PRIMARY_DOMAIN: ${PRIMARY_DOMAIN:-caddyproxymanager.com}
|
|
volumes:
|
|
- caddy-data:/data
|
|
- caddy-config:/config
|
|
- caddy-logs:/logs
|
|
- geoip-data:/usr/share/GeoIP:ro,z
|
|
networks:
|
|
- caddy-network
|
|
healthcheck:
|
|
test: ["CMD", "wget", "--quiet", "--tries=1", "-O", "/dev/null", "http://localhost:2019/config/"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 10s
|
|
|
|
# Docker socket proxy — restricts API surface exposed to l4-port-manager.
|
|
# Only allows GET, POST to /containers/ and /compose/ endpoints.
|
|
docker-socket-proxy:
|
|
container_name: caddy-proxy-manager-docker-proxy
|
|
image: tecnativa/docker-socket-proxy:latest
|
|
restart: unless-stopped
|
|
environment:
|
|
CONTAINERS: 1
|
|
POST: 1
|
|
# Deny everything else by default
|
|
IMAGES: 0
|
|
NETWORKS: 0
|
|
VOLUMES: 0
|
|
EXEC: 0
|
|
SWARM: 0
|
|
AUTH: 0
|
|
SECRETS: 0
|
|
BUILD: 0
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
networks:
|
|
- caddy-network
|
|
|
|
# L4 Port Manager sidecar — automatically recreates the caddy container
|
|
# when L4 proxy host ports change.
|
|
# Uses Docker socket proxy instead of direct Docker socket access.
|
|
l4-port-manager:
|
|
container_name: caddy-proxy-manager-l4-ports
|
|
image: ghcr.io/fuomag9/caddy-proxy-manager-l4-port-manager:latest
|
|
build:
|
|
context: .
|
|
dockerfile: docker/l4-port-manager/Dockerfile
|
|
restart: unless-stopped
|
|
environment:
|
|
DATA_DIR: /data
|
|
COMPOSE_DIR: /compose
|
|
POLL_INTERVAL: "${L4_PORT_MANAGER_POLL_INTERVAL:-2}"
|
|
DOCKER_HOST: tcp://docker-socket-proxy:2375
|
|
volumes:
|
|
- caddy-manager-data:/data
|
|
- .:/compose:ro
|
|
depends_on:
|
|
caddy:
|
|
condition: service_healthy
|
|
docker-socket-proxy:
|
|
condition: service_started
|
|
networks:
|
|
- caddy-network
|
|
|
|
geoipupdate:
|
|
container_name: geoipupdate-${HOSTNAME}
|
|
image: ghcr.io/maxmind/geoipupdate
|
|
profiles: [geoipupdate]
|
|
restart: always
|
|
environment:
|
|
- GEOIPUPDATE_ACCOUNT_ID=${GEOIPUPDATE_ACCOUNT_ID:-}
|
|
- GEOIPUPDATE_LICENSE_KEY=${GEOIPUPDATE_LICENSE_KEY:-}
|
|
- 'GEOIPUPDATE_EDITION_IDS=GeoLite2-ASN GeoLite2-City GeoLite2-Country'
|
|
- GEOIPUPDATE_FREQUENCY=72
|
|
volumes:
|
|
- geoip-data:/usr/share/GeoIP:z
|
|
networks:
|
|
- caddy-network
|
|
|
|
networks:
|
|
caddy-network:
|
|
driver: bridge
|
|
|
|
volumes:
|
|
caddy-manager-data:
|
|
caddy-data:
|
|
caddy-config:
|
|
caddy-logs:
|
|
geoip-data:
|