# Managed by Ansible - roles/jar_lid_archive. Do not edit on the host. # # Same three services as the app repo's compose.yaml, with three differences: # - `web` runs the CI-built image instead of `build: .` # - `web` is on the HOST network, so UFW and the tailnet ACL decide who reaches # gunicorn. A published port would be DNATed by Docker straight past UFW and # answer on the LAN too. See the role README. # - data is bind-mounted under {{ jar_lid_archive_dir }} rather than in named # volumes, so the backup tars a path it can name. # # Secrets are interpolated from .env (0600) beside this file; this file has none. name: jar-lid-archive x-logging: &logging # Docker's default json-file driver never rotates, on a 10 GB disk. driver: json-file options: max-size: "10m" max-file: "3" x-postgres-env: &postgres-env POSTGRES_DB: "{{ jar_lid_archive_postgres_db }}" POSTGRES_USER: "{{ jar_lid_archive_postgres_user }}" POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?} x-s3-env: &s3-env S3_ACCESS_KEY: "{{ jar_lid_archive_s3_access_key }}" S3_SECRET_KEY: ${S3_SECRET_KEY:?} services: web: image: "{{ jar_lid_archive_image }}:{{ jar_lid_archive_image_tag }}" # gunicorn binds 0.0.0.0:{{ jar_lid_archive_port }} - the port is fixed by the image's CMD. network_mode: host environment: <<: [*postgres-env, *s3-env] POSTGRES_HOST: 127.0.0.1 POSTGRES_PORT: "{{ jar_lid_archive_postgres_port }}" S3_ENDPOINT_URL: "http://127.0.0.1:{{ jar_lid_archive_s3_port }}" S3_BUCKET: "{{ jar_lid_archive_s3_bucket }}" DJANGO_DEBUG: "0" DJANGO_SECRET_KEY: ${DJANGO_SECRET_KEY:?} # 127.0.0.1 for the container healthcheck and the probe, which both call # /health/ directly rather than through Caddy. DJANGO_ALLOWED_HOSTS: "{{ jar_lid_archive_domain }},127.0.0.1,localhost" # Caddy terminates TLS and forwards plain HTTP with X-Forwarded-Proto. The # app must trust that header or every POST fails CSRF and share links come # out http://. Safe only because nothing but Caddy can reach gunicorn. DJANGO_BEHIND_TLS_PROXY: "1" DJANGO_CSRF_TRUSTED_ORIGINS: "https://{{ jar_lid_archive_domain }}" depends_on: db: condition: service_healthy storage: condition: service_healthy healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:{{ jar_lid_archive_port }}/health/')"] interval: 30s timeout: 5s retries: 5 start_period: 180s logging: *logging restart: unless-stopped db: image: "{{ jar_lid_archive_postgres_image }}" environment: *postgres-env ports: - "127.0.0.1:{{ jar_lid_archive_postgres_port }}:5432" volumes: - ./postgres:/var/lib/postgresql healthcheck: test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] interval: 5s timeout: 3s retries: 15 start_period: 120s logging: *logging restart: unless-stopped storage: image: "{{ jar_lid_archive_seaweedfs_image }}" environment: *s3-env # Copied from the app's compose.yaml: write the S3 identity from the # environment, then hand over to the image's entrypoint, which drops root # and runs master + volume + filer + S3 gateway. # # The `rm -f` is ours, and load-bearing. On a RESTART of the same container # (a reboot, a backup's stop/start) /tmp/s3.json survives, owned by seaweed # from the chown below. Root cannot then re-create it: /tmp is sticky and # the kernel's protected_regular refuses O_CREAT on another user's file # there. The result was "can't create /tmp/s3.json: Permission denied", an # unhealthy storage container, and web never starting - found on the first # backup run. entrypoint: ["sh", "-c"] command: - | rm -f /tmp/s3.json umask 077 cat > /tmp/s3.json <