# FamilyNido — production compose stack for the home Ubuntu server. # Pulls pre-built images from GHCR (published by .github/workflows/build-and-push.yml) # instead of building on the host. Run from /opt/familynido/ (or wherever this # file lives alongside its .env) with: # # docker compose -f docker-compose.prod.yml pull # docker compose -f docker-compose.prod.yml up -d # # Assumes an external Traefik instance terminates TLS via the configured # cert resolver on the ${TRAEFIK_NETWORK} network. services: db: image: postgres:16-alpine container_name: familynido-db restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} TZ: ${FAMILYNIDO_TIMEZONE} volumes: - familynido-db-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] interval: 10s timeout: 5s retries: 5 networks: - internal api: image: ghcr.io/${GHCR_OWNER}/familynido-api:latest container_name: familynido-api restart: unless-stopped depends_on: db: condition: service_healthy environment: # PUID/PGID let the entrypoint align the in-container `familynido` user # with the host owner of any bind-mounted directory. Leave the .env # values empty to keep the default (system UID 100). Set them to a # specific UID/GID (e.g. 1000:1000) when the files volume is a bind # mount and you want the writes to appear under that host user. PUID: ${PUID:-} PGID: ${PGID:-} ASPNETCORE_ENVIRONMENT: Production ASPNETCORE_HTTP_PORTS: "8080" ASPNETCORE_FORWARDEDHEADERS_ENABLED: "true" ConnectionStrings__Postgres: Host=db;Port=5432;Database=${POSTGRES_DB};Username=${POSTGRES_USER};Password=${POSTGRES_PASSWORD} Oidc__Authority: ${OIDC_AUTHORITY} Oidc__ClientId: ${OIDC_CLIENT_ID} Oidc__ClientSecret: ${OIDC_CLIENT_SECRET} Oidc__RequireHttpsMetadata: ${OIDC_REQUIRE_HTTPS} Family__DefaultTimeZone: ${FAMILYNIDO_TIMEZONE} Cors__AllowedOrigins__0: ${FAMILYNIDO_BASE_URL} Calendar__GoogleClientId: ${GOOGLE_CLIENT_ID} Calendar__GoogleClientSecret: ${GOOGLE_CLIENT_SECRET} Calendar__OAuthRedirectUri: ${GOOGLE_OAUTH_REDIRECT_URI} Email__Enabled: ${EMAIL__ENABLED:-false} Email__SmtpHost: ${EMAIL__SMTPHOST:-} Email__SmtpPort: ${EMAIL__SMTPPORT:-587} Email__SmtpUsername: ${EMAIL__SMTPUSERNAME:-} Email__SmtpPassword: ${EMAIL__SMTPPASSWORD:-} Email__SmtpUseStartTls: ${EMAIL__SMTPUSESTARTTLS:-true} Email__From: ${EMAIL__FROM:-FamilyNido } Email__AppBaseUrl: ${EMAIL__APPBASEURL:-${FAMILYNIDO_BASE_URL}} Email__InvitationLifetime: ${EMAIL__INVITATIONLIFETIME:-7.00:00:00} TZ: ${FAMILYNIDO_TIMEZONE} volumes: - familynido-files:/app/data/files # Persist ASP.NET Core Data Protection keys across container recreations. # Without this, every `docker compose up -d` rotates the keys and all # active OIDC cookies (session + nonce/correlation) become unreadable. - familynido-keys:/home/familynido/.aspnet/DataProtection-Keys networks: - internal labels: - com.centurylinklabs.watchtower.enable=true web: image: ghcr.io/${GHCR_OWNER}/familynido-web:latest container_name: familynido-web restart: unless-stopped depends_on: - api networks: - internal - traefik labels: - com.centurylinklabs.watchtower.enable=true - traefik.docker.network=${TRAEFIK_NETWORK} - traefik.http.routers.${TRAEFIK_ROUTER_NAME}.rule=Host(`${TRAEFIK_HOST}`) - traefik.http.routers.${TRAEFIK_ROUTER_NAME}.tls=true - traefik.http.routers.${TRAEFIK_ROUTER_NAME}.tls.certresolver=${TRAEFIK_TLS_RESOLVER} - traefik.http.services.${TRAEFIK_ROUTER_NAME}.loadbalancer.server.port=8080 volumes: familynido-db-data: familynido-files: familynido-keys: networks: internal: driver: bridge traefik: name: ${TRAEFIK_NETWORK} external: true