familynido/deploy/docker-compose.prod.yml
Pablo Fernández a308041d59 Initial commit
FamilyNido — a self-hosted PWA for a single household: shared calendar,
chores, meals, school agenda, health records and a family wall. One
instance per family, deployable with `docker compose` on any home
server.

Stack: .NET 10 (ASP.NET Core Minimal APIs) + EF Core 10 + PostgreSQL 16
on the backend, Angular 21 (standalone, signals, zoneless) + Tailwind
CSS v4 on the frontend, SignalR for realtime, optional OIDC alongside
local credentials, integration via a versioned `/api/v1/**` public API.

See README.md for the module overview and how to deploy.
2026-05-13 00:23:14 +02:00

108 lines
4.1 KiB
YAML

# 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 <noreply@familynido.example.com>}
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