-
Notifications
You must be signed in to change notification settings - Fork 66
Expand file tree
/
Copy pathdocker-compose-local-dev.yml
More file actions
226 lines (202 loc) · 9.16 KB
/
Copy pathdocker-compose-local-dev.yml
File metadata and controls
226 lines (202 loc) · 9.16 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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# =============================================================================
# Docker Compose Configuration for LOCAL DEVELOPMENT
# =============================================================================
#
# This file defines two services that run together:
# 1. db - A PostgreSQL database container
# 2. website - The Django application container
# As well as a third service that checks website accessibility:
# 3. a11y - An accessibility testing container using Pa11y + Axe
#
# Usage:
# docker compose -f docker-compose-local-dev.yml up
#
# To run accessibility checks, you can edit .pa11yci.json and then run:
# docker compose -f docker-compose-local-dev.yml --profile testing run --rm a11y
#
# Access check with report generation:
# docker compose -f docker-compose-local-dev.yml --profile testing run --rm a11y sh -c "
# npm install -g pa11y-ci &&
# pa11y-ci --config /workspace/.pa11yci.json --json | tee /workspace/a11y-report.json
# "
#
# After running, the website is available at: http://localhost:8571
#
# To stop:
# docker compose down
#
# =============================================================================
services:
# ===========================================================================
# DATABASE SERVICE
# ===========================================================================
# We use the official PostgreSQL Docker image from Docker Hub.
# See: https://hub.docker.com/_/postgres
db:
image: 'postgres:16'
# Restart policy: 'always' means Docker will restart this container
# automatically if it crashes or if Docker itself restarts.
restart: always
ports:
# Port mapping format: 'HOST_PORT:CONTAINER_PORT'
#
# PostgreSQL runs on port 5432 inside the container. We map it to 6543
# on localhost so you can connect with external tools (pgAdmin, DBeaver,
# VS Code extensions, etc.) without conflicting with any local PostgreSQL.
#
# To connect externally: localhost:6543
# The website container connects internally via: db:5432
- '6543:5432'
environment:
# These environment variables configure the PostgreSQL instance.
# The Django app uses these same credentials to connect.
- POSTGRES_DB=makeability # Database name
- POSTGRES_USER=admin # Username
- POSTGRES_PASSWORD=password # Password (fine for local dev, never use in production!)
volumes:
# Named volume for persistent database storage.
# Without this, all data would be lost when the container stops.
# The volume 'postgres-data' is defined at the bottom of this file.
- postgres-data:/var/lib/postgresql/data
healthcheck:
# Healthcheck ensures the DB is ready before the website tries to connect.
# This fixes the "first-time run" migration crashes.
test: ["CMD-SHELL", "pg_isready -U admin -d makeability"]
interval: 10s
timeout: 5s
retries: 5
# ===========================================================================
# DATABASE BACKUP SERVICE (#1443)
# ===========================================================================
# Writes a dated, gzipped pg_dump into the postgres data volume once a day.
#
# On the servers this exists because volume-level snapshots of a *live*
# postgres data directory are only probably restorable; the dump is the
# guaranteed-consistent restore point. It runs locally too so that the same
# code path is exercised in development rather than only in production.
#
# Two things here are load-bearing and should not be "simplified":
# 1. `entrypoint` is overridden. Left alone, the postgres image's own
# entrypoint would try to start a second database server on this PGDATA.
# 2. The scheduling loop lives here, not inside pg_backup.sh, because
# `docker compose up -d` only recreates containers whose config changed.
# A loop inside the bind-mounted script would keep running stale code
# after a deploy.
#
# To force a backup immediately instead of waiting for the next pass:
# docker compose -f docker-compose-local-dev.yml exec db-backup \
# bash /backup-scripts/pg_backup.sh
#
# To verify and restore, see docs/BACKUPS.md.
db-backup:
# Same image as `db` so the pg_dump binary version matches the server.
image: 'postgres:16'
restart: always
environment:
# libpq connection settings; must match the `db` service above.
- PGHOST=db
- PGUSER=admin
- PGPASSWORD=password
- PGDATABASE=makeability
# Delete dumps older than this, but never the most recent one.
- BACKUP_RETENTION_DAYS=14
# How long to wait between passes, and after a failed pass.
- BACKUP_POLL_SECONDS=3600
- BACKUP_RETRY_SECONDS=300
volumes:
# The database volume, so dumps land inside the thing that gets snapshotted.
- postgres-data:/var/lib/postgresql/data
# Small shared volume for the status file Django reads.
- backup-status:/var/backup-status
# The backup script, mounted as a directory so edits survive git checkouts.
- ./scripts:/backup-scripts:ro
# `sleep ... & wait $!` plus the leading `trap`, instead of a plain
# foreground `sleep`, so `docker compose stop`/`down` return promptly:
# bash only acts on a trapped signal between foreground commands, so a
# SIGTERM arriving during a plain `sleep 3600` would sit unhandled for up
# to an hour and this container would always eat the full stop grace
# period before Docker escalates to SIGKILL.
entrypoint: ["/bin/bash", "-c", "trap 'exit 0' TERM INT; while true; do if bash /backup-scripts/pg_backup.sh; then sleep \"$${BACKUP_POLL_SECONDS}\" & wait $!; else sleep \"$${BACKUP_RETRY_SECONDS}\" & wait $!; fi; done"]
depends_on:
db:
condition: service_healthy
# ===========================================================================
# WEBSITE SERVICE (Django Application)
# ===========================================================================
website:
# Build the image using the Dockerfile in the current directory.
build:
context: .
dockerfile: Dockerfile
restart: always
ports:
# Map container port 8000 (Django's default) to localhost:8571.
# The '127.0.0.1:' prefix ensures only local connections are accepted
# (not connections from other machines on your network).
- '127.0.0.1:8571:8000'
volumes:
# Mount the current directory (.) to /code inside the container.
# This enables "live reloading"—when you edit local files, the changes
# are immediately visible inside the container without rebuilding.
- .:/code
# Read-only view of the backup status file so Django can report backup
# health on the admin dashboard and /version.json (#1443).
- backup-status:/var/backup-status:ro
healthcheck:
# Check if Django is responding
test: ["CMD-SHELL", "curl -f http://localhost:8000/ || exit 1"]
interval: 10s
timeout: 5s
retries: 12
start_period: 90s # Give Django time for migrations on first run
# Wait for the database to be "healthy" (fully started) before running
depends_on:
db:
condition: service_healthy
# The command to run when the container starts.
# This executes the docker-entrypoint.sh script, which handles:
# - Collecting static files
# - Running database migrations
# - Starting the Django development server
command: ["./docker-entrypoint.sh"]
# ===========================================================================
# ACCESSIBILITY TESTING SERVICE (Pa11y + Axe)
# ===========================================================================
# Runs automated accessibility scans against the website container.
# Uses the Axe engine (same core engine as DubBot/Deque tools).
#
# Usage:
# docker-compose -f docker-compose-local-dev.yml run --rm a11y
#
# Note: The website must be running first. Start it with:
# docker-compose -f docker-compose-local-dev.yml up -d website
# ===========================================================================
a11y:
# Puppeteer image includes Chrome and all required dependencies
image: ghcr.io/puppeteer/puppeteer:latest
user: root # Needed for npm global install
# Install pa11y-ci and run it with our config file
command: >
sh -c "
npm install -g pa11y-ci &&
pa11y-ci --config /workspace/.pa11yci.json
"
volumes:
# Mount entire project directory for config input and report output
- .:/workspace
depends_on:
website:
condition: service_healthy
# Optional: Prevent running during normal 'docker-compose up'
profiles:
- testing
# =============================================================================
# NAMED VOLUMES
# =============================================================================
# Named volumes persist data outside the container lifecycle.
# Run 'docker volume ls' to see all volumes.
# Run 'docker volume rm postgres-data' to delete (WARNING: destroys all data).
volumes:
postgres-data:
# Disposable: holds only the backup status.json, regenerated every pass.
backup-status: