-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
146 lines (138 loc) · 4.59 KB
/
Copy pathdocker-compose.yml
File metadata and controls
146 lines (138 loc) · 4.59 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
x-api-build: &api-build
context: .
dockerfile: docker/Dockerfile.backend
x-api-environment: &api-environment
DATABASE_URL: postgresql+asyncpg://db2api:db2api@db:5432/db2api
APP_ENV: development
DEBUG: "false"
LOG_LEVEL: INFO
CORS_ORIGINS: "http://localhost:5173,http://localhost:80"
JWT_SECRET_KEY: ${JWT_SECRET_KEY?JWT_SECRET_KEY is required}
JWT_ALGORITHM: HS256
JWT_ACCESS_TOKEN_EXPIRE_MINUTES: "60"
QUERY_TIMEOUT_SECONDS: "30"
ENCRYPTION_KEY: ${ENCRYPTION_KEY?ENCRYPTION_KEY is required}
ADMIN_USERNAME: ${ADMIN_USERNAME?ADMIN_USERNAME is required}
ADMIN_PASSWORD_HASH: ${ADMIN_PASSWORD_HASH?ADMIN_PASSWORD_HASH is required}
# Leave blank for thin mode. For thick mode, the bundled Linux client lives
# at /opt/oracle/instantclient_19_32.
ORACLE_CLIENT_LIB_DIR: ${ORACLE_CLIENT_LIB_DIR:-}
services:
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: db2api
POSTGRES_USER: db2api
POSTGRES_PASSWORD: db2api
ports:
# Loopback only: the API reaches Postgres over the internal `backend`
# network. This host mapping exists solely for local dev tools
# (psql/GUI clients) and must never be published on external
# interfaces — the data tier holds encrypted credentials, signing
# secrets, and access logs.
- "127.0.0.1:5432:5432"
volumes:
- db_data:/var/lib/postgresql/data
# Data tier only — the API reaches Postgres here; nginx never does.
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U db2api -d db2api"]
interval: 10s
timeout: 5s
retries: 5
migrate:
build: *api-build
restart: "no"
command: ["alembic", "upgrade", "head"]
environment: *api-environment
networks:
- backend
depends_on:
db:
condition: service_healthy
api:
build: *api-build
restart: unless-stopped
environment:
<<: *api-environment
# Trust forwarded headers ONLY from the dedicated nginx<->api "edge"
# subnet. nginx is the sole member of that subnet besides this service,
# so db/oracle (on the separate "backend" network) cannot reach :8000
# to forge X-Forwarded-For even if compromised. Must match the edge
# network's subnet below.
FORWARDED_ALLOW_IPS: "10.31.0.0/24"
# The backend is intentionally NOT published to the host: it is reached
# only through the nginx reverse proxy (`web`). It sits on two segmented
# networks — `edge` (shared only with nginx, the trusted proxy hop) and
# `backend` (shared only with the data tier). Use `http://localhost/api/...`
# (via `web`) in dev, or `docker compose exec api ...` for direct debugging.
expose:
- "8000"
networks:
- edge
- backend
depends_on:
db:
condition: service_healthy
migrate:
condition: service_completed_successfully
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8000/api/v1/admin/health/live || exit 1"]
interval: 15s
timeout: 5s
retries: 5
start_period: 30s
web:
build:
context: .
dockerfile: docker/Dockerfile.frontend
restart: unless-stopped
ports:
- "80:80"
# Edge tier only — nginx is the trusted proxy hop and the single member of
# the edge subnet that talks to the API. It has no route to the data tier.
networks:
- edge
depends_on:
api:
condition: service_healthy
# Optional Oracle XE service — activate with: docker compose --profile oracle up
oracle:
image: gvenzl/oracle-xe:21-slim
profiles:
- oracle
restart: unless-stopped
environment:
ORACLE_PASSWORD: ${ORACLE_PASSWORD:-oracle}
ports:
# Loopback only — see the db service rationale. The API connects over
# the internal `backend` network; this is for local dev access only.
- "127.0.0.1:1521:1521"
volumes:
- oracle_data:/opt/oracle/oradata
# Data tier only — reachable by the API, never by nginx.
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "healthcheck.sh"]
interval: 30s
timeout: 10s
retries: 10
start_period: 120s
networks:
# nginx <-> api only. Fixed subnet so the backend can scope
# FORWARDED_ALLOW_IPS to exactly this hop (see the `api` service).
edge:
driver: bridge
ipam:
config:
- subnet: 10.31.0.0/24
# api <-> data tier (Postgres, Oracle). nginx is NOT attached, so a
# compromised data container cannot reach the API to spoof forwarded headers.
backend:
driver: bridge
volumes:
db_data:
oracle_data: