195 lines
6.1 KiB
YAML
195 lines
6.1 KiB
YAML
# Packet Station base stack: postgres, a one-shot migrate, the app and the worker.
|
|||
|
|
# One file for test and production. The env file picks the stack.
|
||
|
|
#
|
||
|
|
# Production on docker-1:
|
||
|
|
# docker compose -p packetstation --env-file /etc/packetstation/prod/stack.env \
|
||
|
|
# -f deploy/compose.yaml -f deploy/compose.proxy.yaml up -d
|
||
|
|
# Test on docker-1:
|
||
|
|
# docker compose -p packetstation-test --env-file /etc/packetstation/test/stack.env \
|
||
|
|
# -f deploy/compose.yaml -f deploy/compose.proxy.yaml up -d
|
||
|
|
#
|
||
|
|
# No service publishes a port. NPM reaches the app over npm_default (compose.proxy.yaml).
|
||
|
|
# PostgreSQL is only on the internal backend network.
|
||
|
|
|
||
|
|
# The project name comes from the env file so prod and test can never share volumes by accident.
|
||
|
|
# `docker compose -p` still wins when given.
|
||
|
|
name: ${PROJECT_NAME:?set PROJECT_NAME in the stack env file}
|
||
|
|
|
||
|
|
x-logging: &logging
|
||
|
|
driver: json-file
|
||
|
|
options:
|
||
|
|
max-size: "10m"
|
||
|
|
max-file: "5"
|
||
|
|
|
||
|
|
# Hardening shared by the three image services. The root filesystem is read-only;
|
||
|
|
# /tmp is a small tmpfs for Python temp files and the worker beat file.
|
||
|
|
x-hardening: &hardening
|
||
|
|
read_only: true
|
||
|
|
tmpfs:
|
||
|
|
- /tmp:size=64m,mode=1777
|
||
|
|
security_opt:
|
||
|
|
- no-new-privileges:true
|
||
|
|
cap_drop:
|
||
|
|
- ALL
|
||
|
|
|
||
|
|
services:
|
||
|
|
postgres:
|
||
|
|
# postgres:16-bookworm pinned by digest (multi-arch index, looked up 2026-10-15).
|
||
|
|
# To refresh: docker buildx imagetools inspect postgres:16-bookworm
|
||
|
|
# and copy the top-level "Digest:" line here. Test on the test stack first.
|
||
|
|
image: postgres:16-bookworm@sha256:0ea6700a3b4f0ae6ce746519073558aed4d88a79d8d07622a9a644946c7319c4
|
||
|
|
# uid 999 is the image's postgres user. Group 10001 lets it read the
|
||
|
|
# root:10001 0440 secret files without running as root.
|
||
|
|
user: "999:10001"
|
||
|
|
environment:
|
||
|
|
POSTGRES_DB: postgres # 01-roles.sh creates the packetstation database.
|
||
|
|
POSTGRES_USER: postgres
|
||
|
|
POSTGRES_PASSWORD_FILE: /run/secrets/postgres_superuser_password
|
||
|
|
PGDATA: /var/lib/postgresql/data/pgdata
|
||
|
|
TZ: UTC
|
||
|
|
secrets:
|
||
|
|
- postgres_superuser_password
|
||
|
|
- postgres_migrate_password
|
||
|
|
- postgres_app_password
|
||
|
|
volumes:
|
||
|
|
- pgdata:/var/lib/postgresql/data
|
||
|
|
- ./postgres/initdb:/docker-entrypoint-initdb.d:ro
|
||
|
|
networks:
|
||
|
|
backend:
|
||
|
|
aliases:
|
||
|
|
- packetstation-postgres # PG_HOST. Unique so it cannot collide with another stack.
|
||
|
|
shm_size: 256m
|
||
|
|
security_opt:
|
||
|
|
- no-new-privileges:true
|
||
|
|
healthcheck:
|
||
|
|
test: ["CMD-SHELL", "pg_isready -U postgres -d postgres -h 127.0.0.1"]
|
||
|
|
interval: 10s
|
||
|
|
timeout: 5s
|
||
|
|
retries: 6
|
||
|
|
start_period: 30s
|
||
|
|
restart: unless-stopped
|
||
|
|
logging: *logging
|
||
|
|
|
||
|
|
migrate:
|
||
|
|
image: ${IMAGE:?set IMAGE in the stack env file}
|
||
|
|
command: ["migrate"]
|
||
|
|
env_file:
|
||
|
|
- ${ENV_FILE:?set ENV_FILE in the stack env file}
|
||
|
|
environment:
|
||
|
|
# Fixed in-container paths. These win over the env file, where BACKUP_DIR is the host path.
|
||
|
|
ASSETS_DIR: /data/assets
|
||
|
|
BACKUP_DIR: /backups
|
||
|
|
STATIC_DIR: /app/static
|
||
|
|
secrets:
|
||
|
|
- postgres_migrate_password
|
||
|
|
- postgres_app_password
|
||
|
|
networks:
|
||
|
|
backend: {}
|
||
|
|
depends_on:
|
||
|
|
postgres:
|
||
|
|
condition: service_healthy
|
||
|
|
<<: *hardening
|
||
|
|
restart: "no"
|
||
|
|
logging: *logging
|
||
|
|
|
||
|
|
app:
|
||
|
|
image: ${IMAGE:?set IMAGE in the stack env file}
|
||
|
|
command: ["app"]
|
||
|
|
env_file:
|
||
|
|
- ${ENV_FILE:?set ENV_FILE in the stack env file}
|
||
|
|
environment:
|
||
|
|
# Fixed in-container paths. These win over the env file, where BACKUP_DIR is the host path.
|
||
|
|
ASSETS_DIR: /data/assets
|
||
|
|
BACKUP_DIR: /backups
|
||
|
|
STATIC_DIR: /app/static
|
||
|
|
secrets:
|
||
|
|
- postgres_app_password
|
||
|
|
- ldap_bind_password
|
||
|
|
- session_key
|
||
|
|
- seal_key
|
||
|
|
volumes:
|
||
|
|
- assets:/data/assets
|
||
|
|
- ${CA_BUNDLE_FILE:?set CA_BUNDLE_FILE in the stack env file}:/etc/ssl/pesco/ca.pem:ro
|
||
|
|
networks:
|
||
|
|
backend: {}
|
||
|
|
egress: {}
|
||
|
|
depends_on:
|
||
|
|
migrate:
|
||
|
|
condition: service_completed_successfully
|
||
|
|
healthcheck:
|
||
|
|
test: ["CMD", "packetstation", "healthcheck", "app"]
|
||
|
|
interval: 30s
|
||
|
|
timeout: 5s
|
||
|
|
retries: 3
|
||
|
|
start_period: 30s
|
||
|
|
<<: *hardening
|
||
|
|
mem_limit: 1g
|
||
|
|
restart: unless-stopped
|
||
|
|
logging: *logging
|
||
|
|
|
||
|
|
worker:
|
||
|
|
image: ${IMAGE:?set IMAGE in the stack env file}
|
||
|
|
command: ["worker"]
|
||
|
|
env_file:
|
||
|
|
- ${ENV_FILE:?set ENV_FILE in the stack env file}
|
||
|
|
environment:
|
||
|
|
# Fixed in-container paths. These win over the env file, where BACKUP_DIR is the host path.
|
||
|
|
ASSETS_DIR: /data/assets
|
||
|
|
BACKUP_DIR: /backups
|
||
|
|
STATIC_DIR: /app/static
|
||
|
|
secrets:
|
||
|
|
- postgres_app_password
|
||
|
|
- visualkit_password
|
||
|
|
- mfileskit_password
|
||
|
|
- ldap_bind_password
|
||
|
|
- seal_key
|
||
|
|
volumes:
|
||
|
|
- assets:/data/assets
|
||
|
|
- ${CA_BUNDLE_FILE:?set CA_BUNDLE_FILE in the stack env file}:/etc/ssl/pesco/ca.pem:ro
|
||
|
|
- ${BACKUP_DIR:?set BACKUP_DIR in the stack env file}:/backups:ro
|
||
|
|
networks:
|
||
|
|
backend: {}
|
||
|
|
egress: {}
|
||
|
|
depends_on:
|
||
|
|
migrate:
|
||
|
|
condition: service_completed_successfully
|
||
|
|
healthcheck:
|
||
|
|
test: ["CMD", "packetstation", "healthcheck", "worker"]
|
||
|
|
interval: 30s
|
||
|
|
timeout: 5s
|
||
|
|
retries: 3
|
||
|
|
start_period: 60s
|
||
|
|
<<: *hardening
|
||
|
|
mem_limit: 1g
|
||
|
|
restart: unless-stopped
|
||
|
|
logging: *logging
|
||
|
|
|
||
|
|
networks:
|
||
|
|
backend:
|
||
|
|
internal: true # No route out. postgres lives only here.
|
||
|
|
egress:
|
||
|
|
driver: bridge # Outbound to VISUAL, M-Files, LDAP and SMTP.
|
||
|
|
|
||
|
|
volumes:
|
||
|
|
pgdata: {}
|
||
|
|
assets: {}
|
||
|
|
|
||
|
|
# Host files, root:10001 mode 0440, one value per file, no trailing carriage return.
|
||
|
|
secrets:
|
||
|
|
postgres_superuser_password:
|
||
|
|
file: ${SECRETS_DIR:?set SECRETS_DIR in the stack env file}/postgres_superuser_password
|
||
|
|
postgres_migrate_password:
|
||
|
|
file: ${SECRETS_DIR}/postgres_migrate_password
|
||
|
|
postgres_app_password:
|
||
|
|
file: ${SECRETS_DIR}/postgres_app_password
|
||
|
|
visualkit_password:
|
||
|
|
file: ${SECRETS_DIR}/visualkit_password
|
||
|
|
mfileskit_password:
|
||
|
|
file: ${SECRETS_DIR}/mfileskit_password
|
||
|
|
ldap_bind_password:
|
||
|
|
file: ${SECRETS_DIR}/ldap_bind_password
|
||
|
|
session_key:
|
||
|
|
file: ${SECRETS_DIR}/session_key
|
||
|
|
seal_key:
|
||
|
|
file: ${SECRETS_DIR}/seal_key
|