Files
homelabstack/stacks/resume/docker-compose.yml
T
ginnoirandClaude Opus 5 fb0b692f83 fix(resume): add ENCRYPTION_SECRET + redis so the AI tab loads
The v5 rewrite gates AI features behind two new env vars we never had.
Opening the AI Integrations tab calls aiProviders.list, which runs
assertCredentialEncryptionConfigured() and throws
AI_CREDENTIAL_ENCRYPTION_UNAVAILABLE when ENCRYPTION_SECRET is unset --
so the tab errored on mount instead of rendering.

The agent workspace has a second gate: isAgentEnvironmentConfigured()
requires ENCRYPTION_SECRET *and* REDIS_URL, so add a dedicated redis to
the stack rather than leaving the agent half-broken. Every other redis
on the host is on another stack's private network.

ENCRYPTION_SECRET encrypts stored provider API keys at rest (schema
requires >=32 chars); rotating it makes saved keys undecryptable, so
it is pinned like AUTH_SECRET.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 15:27:27 -05:00

139 lines
4.3 KiB
YAML

# resume stack — Reactive Resume app + Postgres, MinIO, and the Chrome printer.
#
# Volume -> bind-mount conversions (tiered per density policy):
# postgres data -> /config/resume/postgres (SSD; DB, low density)
# minio data -> /storage1/labdata/resume/minio (ZFS; object store, blobs)
# redis data -> /config/resume/redis (SSD; AI agent stream state)
# app uploads -> /storage1/labdata/resume/data (ZFS; blobs, see app below)
# The Chrome service is stateless. app + resume-minio join edge (resume.ginnoir.com,
# storage.j-costa.com, minio.ginnoir.com); postgres stays private.
services:
postgres:
container_name: postgres_resume
image: postgres:16-alpine
restart: unless-stopped
labels:
- "com.centurylabs.watchtower.enable=false"
networks: [resume]
volumes:
- /config/resume/postgres:/var/lib/postgresql/data
env_file:
- stack.env
environment:
- POSTGRES_DB=postgres
- POSTGRES_USER=postgres
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
interval: 10s
timeout: 5s
retries: 5
resume-minio:
container_name: minio_resume
image: minio/minio:RELEASE.2025-09-07T16-13-09Z
restart: unless-stopped
labels:
- "com.centurylabs.watchtower.enable=false"
networks: [resume, edge]
command: server /data --console-address :9001
ports:
- "9000:9000"
- "9004:9001"
volumes:
- /storage1/labdata/resume/minio:/data
env_file:
- stack.env
environment:
- MINIO_BROWSER_REDIRECT_URL=https://minio.ginnoir.com
- MINIO_IDENTITY_OPENID_CONFIG_URL=https://auth.ginnoir.com/application/o/minio/.well-known/openid-configuration
- MINIO_IDENTITY_OPENID_CLIENT_ID
- MINIO_IDENTITY_OPENID_CLIENT_SECRET
- MINIO_IDENTITY_OPENID_SCOPES=openid,profile,email
healthcheck:
test: ["CMD-SHELL", "curl -sf http://localhost:9000/minio/health/live || exit 1"]
interval: 30s
timeout: 20s
retries: 3
start_period: 30s
chrome:
container_name: chrome
image: ghcr.io/browserless/chromium:latest
restart: unless-stopped
networks: [resume]
env_file:
- stack.env
environment:
- TIMEOUT=10000
- CONCURRENT=10
- EXIT_ON_HEALTH_FAILURE=true
- PRE_REQUEST_HEALTH_CHECK=true
redis:
container_name: redis_resume
image: redis:7-alpine
restart: unless-stopped
labels:
- "com.centurylabs.watchtower.enable=false"
networks: [resume]
command: --save 60 1 --loglevel warning
volumes:
- /config/resume/redis:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
app:
container_name: resume
image: amruthpillai/reactive-resume:latest
restart: unless-stopped
networks: [resume, edge]
ports:
- "3000:3000"
volumes:
# The v5 rewrite stores uploads on the local filesystem
# (LOCAL_STORAGE_PATH=/app/data is baked into the image) and ignores the
# STORAGE_*/MinIO vars below — /api/health reports storage type "local".
# Without this bind, avatars and exports sit in the container's writable
# layer and are lost on every recreate, including Watchtower's.
- /storage1/labdata/resume/data:/app/data
depends_on:
postgres:
condition: service_healthy
resume-minio:
condition: service_healthy
redis:
condition: service_healthy
chrome:
condition: service_started
env_file:
- stack.env
environment:
- PORT=3000
- NODE_ENV=production
- APP_URL=https://resume.ginnoir.com
- STORAGE_URL=https://storage.j-costa.com/default
- PRINTER_ENDPOINT=http://chrome:3000
- MAIL_FROM=noreply@localhost
- STORAGE_ENDPOINT=resume-minio
- STORAGE_PORT=9000
- STORAGE_REGION=us-east-1
- STORAGE_BUCKET=default
- STORAGE_USE_SSL=false
- STORAGE_SKIP_BUCKET_CHECK=false
- OAUTH_PROVIDER_NAME=Authentik
- OAUTH_DISCOVERY_URL=https://auth.ginnoir.com/application/o/resume/.well-known/openid-configuration
- FLAG_DISABLE_EMAIL_AUTH=true
- FLAG_DISABLE_SIGNUPS=true
networks:
resume:
name: resume
driver: bridge
edge:
name: edge
external: true