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.
108 lines
4.1 KiB
YAML
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
|