Repository navigation
Expand file tree
/
Copy pathdocker-compose.yaml
More file actions
132 lines (129 loc) · 6.05 KB
/
Copy pathdocker-compose.yaml
File metadata and controls
132 lines (129 loc) · 6.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# The whole deployment: a weekly pipeline and a static file server, on one host.
#
# Coolify's server-level proxy terminates TLS and routes by Host, so no ports are
# published and no certificates are managed here. Attach the domain to the `files`
# service on port 80.
#
# The scd2 history is the one artefact here that cannot be rebuilt: upstream serves
# only today's export. So it lives in Postgres, and the thing that matters about
# where Postgres is declared is whether Coolify will back it up.
#
# VERIFY AFTER THE FIRST DEPLOY: the `history` service below should show a
# **Backups** link in Coolify. Set a cron and an S3 destination there. Coolify
# supports backups for compose-defined databases and, since January 2026, restoring
# them too -- but integrated backups for *git-based* compose deployments are still
# an open enhancement (coollabsio/coolify#7528), and this stack is git-based because
# `pipeline` builds from the repo.
#
# If no Backups link appears, move the database out rather than run unbacked:
#
# 1. Coolify -> New Resource -> PostgreSQL, and enable its Backups tab
# 2. delete the `history` service here
# 3. point POSTGRES_CONN_STR at the new resource
#
# That is the only change needed. Nothing reaches the database except through
# POSTGRES_CONN_STR, and it is never exposed publicly -- no ports are published, so
# it is reachable only from within this stack.
services:
history:
image: postgres:17-alpine
restart: unless-stopped
environment:
- POSTGRES_DB=${POSTGRES_DB:-fdb}
- POSTGRES_USER=${POSTGRES_USER:-fdb}
# No default. An unset password would make trust authentication the fallback,
# and Coolify will generate one if this is left for it to fill.
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?generate a password in Coolify}
volumes:
- history_data:/var/lib/postgresql/data
healthcheck:
# -U so the check authenticates as the right role rather than the OS user.
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER:-fdb} -d $${POSTGRES_DB:-fdb}"]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
# Runs the weekly load and rebuilds the distribution. Idle otherwise: driven by
# a Coolify Scheduled Task rather than a cron inside the container, so a failed
# run is visible in Coolify's logs instead of a container nobody looks at.
#
# Coolify -> this resource -> Scheduled Tasks
# container: pipeline
# command: python scripts/build_dist.py --out /srv/public
# frequency: 0 3 * * 1
#
# That frequency is not only an operational choice: the published history is
# derived from the loads that match it, so `history.REGULAR_WEEKDAY` and
# `history.REGULAR_HOUR` have to agree with it. Changing the schedule in
# Coolify without changing them relabels every date in the dataset. Coolify's
# containers run UTC, so this fires at 03:00 UTC -- 05:00 Berlin -- and does
# not move with DST.
pipeline:
build: .
restart: unless-stopped
# Nothing to do until the schedule fires. Kept up so the scheduled task has a
# container to exec into.
command: ["sleep", "infinity"]
depends_on:
history:
condition: service_healthy
environment:
# Built from the parts above so the credentials are stated once. Replace the
# whole value with an external URL if the database moves out of this stack.
# Unset, the pipeline would fall back to a local DuckDB file -- right for a
# laptop, wrong here, so it is required.
- POSTGRES_CONN_STR=postgresql://${POSTGRES_USER:-fdb}:${POSTGRES_PASSWORD:?generate a password in Coolify}@history:5432/${POSTGRES_DB:-fdb}
# dlt's working directory. Recreatable scratch, unlike the history, but it
# must persist across runs for load ids and pipeline state to line up.
- DLT_DATA_DIR=/srv/state/dlt
# The keyword segmenter, both optional. Without them the load skips the
# segmentation and keywords_extracted publishes as null for anything not
# already segmented -- see fdb_scraper.history.tagger_configured.
- FDB_TAGGER_URL=${FDB_TAGGER_URL:-}
- FDB_TAGGER_TOKEN=${FDB_TAGGER_TOKEN:-}
# The segmenter client's read-through cache. On the state volume because the
# image's working directory is not writable by the pipeline's user.
- FDB_KEYWORD_CACHE=/srv/state/keyword_cache.sqlite
volumes:
# Read-write: this is the only thing that writes the published files.
# Path is hardcoded because Coolify's compose processor rejects `${...}` in
# volume source paths (it flags them as command-injection vectors), and the
# smoke test overrides this list in compose.smoke.yaml.
- type: bind
source: /data/fdb
target: /srv
files:
# The Caddyfile is baked into the image via Dockerfile.caddy rather than
# bind-mounted. A bind mount would point at ./Caddyfile, which Coolify
# rewrites to <application-dir>/Caddyfile -- and if Coolify has ever created
# an empty `Caddyfile` directory there (which it does when initialising the
# resource), the file in the repo cannot overwrite it and the mount fails at
# container start with "not a directory".
build:
context: .
dockerfile: Dockerfile.caddy
restart: unless-stopped
volumes:
# Only `public` is mounted, deliberately. The sibling `state` directory holds
# dlt's working files, which include extracted copies of the export; Caddy
# serves its root with directory listings on, so mounting /data/fdb here
# would publish them.
#
# Read-only, so a compromised Caddy cannot rewrite what it serves.
- type: bind
source: /data/fdb/public
target: /srv
read_only: true
- caddy_data:/data
- caddy_config:/config
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1/"]
interval: 30s
timeout: 5s
retries: 3
volumes:
# The history. A named volume rather than a bind mount so Coolify manages it, and
# deliberately not under /data/fdb, which the file server mounts.
history_data:
caddy_data:
caddy_config: