Skip to content
This repository was archived by the owner on Jul 20, 2026. It is now read-only.

Commit 6515687

Browse files
committed
Docker configuration done
1 parent 68cb3c6 commit 6515687

7 files changed

Lines changed: 164 additions & 36 deletions

File tree

.env.example

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,21 @@
33
POSTGRES_HOST=postgres
44
POSTGRES_PORT=5432
55
POSTGRES_DB=efes-db
6-
POSTGRES_USER=
7-
POSTGRES_PASSWORD=
6+
POSTGRES_ROOT_USER=
7+
POSTGRES_ROOT_PASSWORD=
8+
POSTGRES_APP_USER=
9+
POSTGRES_APP_PASSWORD=
810

9-
# MongoDB (inside the Docker network)
10-
MONGO_URI=mongodb://mongo:27017/efes-db
11+
# MongoDB
12+
MONGO_DB=efes-db
13+
MONGO_ROOT_USER=
14+
MONGO_ROOT_PASSWORD=
15+
MONGO_APP_USER=
16+
MONGO_APP_PASSWORD=
1117

1218
# Container ports (what is exposed to the host machine)
1319
DOCKER_PORT_FRONTEND=4200
1420
DOCKER_PORT_BACKEND=3000
15-
DOCKER_PORT_POSTGRES=5432
16-
DOCKER_PORT_MONGO=27017
1721

1822
# Docker Compose profiles
1923
COMPOSE_PROFILES=prod

README.md

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,15 @@
88
| `frontend-dev` | Custom (dev target) | Runs `ng serve` on port 4200. Mounts local `src/` and `public/` for hot reload. |
99
| `backend` | Custom (prod target) | Runs compiled Node app on port 3000. Reads secrets from `.env` via `env_file`. |
1010
| `backend-dev` | Custom (dev target) | Runs `npm run dev` (`tsx watch`) on port 3000. Mounts local `backend/` for hot reload. |
11-
| `postgres` | `postgres:16-alpine` | Persists DB files in `postgres-data` volume. Exposes port 5432. |
12-
| `mongo` | `mongo:7` | Persists DB files in `mongodb-data` volume. Exposes port 27017. |
11+
| `postgres` | `postgres:16-alpine` | Persists DB files in `postgres-data` volume. |
12+
| `mongo` | `mongo:7` | Persists DB files in `mongodb-data` volume. |
13+
| `flyway` | `flyway:latest` | Runs migrations before starting the backend. |
1314

14-
Container names are prefixed `efes-` (e.g. `frontend` -> `efes-frontend`).
15+
> [!NOTE]
16+
> Container names are prefixed `efes-` (e.g. `frontend` -> `efes-frontend`).
17+
18+
> [!CAUTION]
19+
> Starting both production and development containers will result in undefined behavior. Please make sure you stop containers with the command `docker compose --profile <profile_name> stop`.
1520
1621
## Ports
1722

@@ -22,26 +27,35 @@ Host ports are set in your `.env` as `DOCKER_PORT_*` variables. Container-side p
2227
| Frontend (prod) | `DOCKER_PORT_FRONTEND` | `80` (Nginx) |
2328
| Frontend (dev) | `DOCKER_PORT_FRONTEND` | `4200` (ng serve) |
2429
| Backend | `DOCKER_PORT_BACKEND` | `3000` |
25-
| Postgres | `DOCKER_PORT_POSTGRES` | `5432` |
26-
| Mongo | `DOCKER_PORT_MONGO` | `27017` |
2730

28-
> **Note:** Inside the Docker network, services talk to each other by service name (e.g. `http://backend:3000`), not `localhost`.
31+
> [!NOTE]
32+
> Inside the Docker network, services talk to each other by service name (e.g. `http://backend:3000`), not `localhost`.
33+
34+
> [!IMPORTANT]
35+
> Both `postgres` and `mongo` are not exposed as it having those programs already installed onto the host may interfere if not configured properly. If you want to access the shell or run commands, please use `docker compose exec <service_name> <bash|cmd>`.
2936
3037
## Volumes
3138

3239
| Volume | Mounted to | Purpose |
3340
|---|---|---|
3441
| `postgres-data` | `/var/lib/postgresql/data` | Persist Postgres DB files |
3542
| `mongodb-data` | `/data/db` | Persist Mongo DB files |
36-
| `backend_node_modules` | `/app/node_modules` | Preserve node_modules across dev rebuilds |
43+
| `mongodb-configdb` | `/data/configdb` | Persist Mongo DB config files |
44+
| `backend-node-modules` | `/app/node_modules` | Preserve node_modules across dev rebuilds |
3745

3846
Local mounts used in dev:
3947

40-
| Local path | Container path | Service |
41-
|---|---|---|
42-
| `./frontend/src` | `/app/src` | `frontend-dev` |
43-
| `./frontend/public` | `/app/public` | `frontend-dev` |
44-
| `./backend` | `/app` | `backend-dev` |
48+
| Local path | Container path | Service | Purpose |
49+
|---|---|---|---|
50+
| `./frontend/src` | `/app/src` | `frontend-dev` | Mount app source for `ng serve` so code edits are reflected immediately |
51+
| `./frontend/public` | `/app/public` | `frontend-dev` | Mount static assets |
52+
| `./backend` | `/app` | `backend-dev` | Mount backend source for `tsx watch` so the server restarts in code changes |
53+
| `./backend/db/init` | `/docker-entrypoint-initdb.d` | `postgres` | Bootstrap SQL/shell scripts executed once on a fresh postgres volume |
54+
| `./backend/db/mongo` | `/docker-entrypoint-initdb.d` | `mongo` | JavaScript init scripts executed once on a fresh mongo volume |
55+
| `./backend/db/migrations` | `/flyway/sql` | `flyway` | Versioned SQL migration files read by Flyway |
56+
57+
> [!NOTE]
58+
> In production, those folders are not mounted. Instead the service the files it has within its image.
4559
4660
## Network
4761

@@ -51,6 +65,7 @@ All services share the custom bridge network `app-network`. Cross-container DNS
5165

5266
- **Postgres** - `pg_isready` healthcheck (see `docker-compose.yml`)
5367
- **Mongo** - `mongosh ping` healthcheck
68+
- **Flyway** - runs once to migrate the database
5469
- `backend` and `backend-dev` both declare `depends_on` with `condition: service_healthy`, so Compose waits for DB readiness before starting them.
5570

5671
## How the frontend reaches the API
@@ -73,6 +88,12 @@ Browser JS should always call same-origin paths like `/api/health`. The dev serv
7388
# Commands
7489
Open a terminal window from the project root and make sure Docker Desktop is started.
7590

91+
> [!NOTE]
92+
> The default profile is `prod`. Thus the following commands are equivalent: `docker compose --profile prod <...>` and `docker compose <...>`.
93+
94+
> [!IMPORTANT]
95+
> When you enter a command for a given profile, the "counter-command" should also include the profile. Failure to do so may yield networking errors or containers may fail to stop/remove themselves.
96+
7697
## Start
7798

7899
| Description | Command |
@@ -111,8 +132,14 @@ Open a terminal window from the project root and make sure Docker Desktop is sta
111132

112133
| Description | Command |
113134
|---|---|
114-
| Open a shell in a container | `docker compose exec frontend-dev sh` |
135+
| Open a shell in a container | `docker compose exec -it frontend-dev sh` |
115136
| Run a command in a container | `docker compose exec frontend-dev <cmd>` |
137+
| Open `mongosh` | `docker compose exec mongo mongosh -u <root_user> -p <root_password>` |
138+
| Open `psql` | `docker compose exec postgres psql -U <root_user> -d <db_name> -W` |
139+
| Migrate | `docker compose run --rm flyway` |
140+
| Repair migrations | `docker compose run --rm flyway repair` |
141+
| Validate migrations | `docker compose run --rm flyway validate` |
142+
| Info on migrations | `docker compose run --rm flyway info` |
116143

117144
## Status
118145

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/bin/sh
2+
set -eu
3+
4+
# The Postgres official image already creates the database named by POSTGRES_DB
5+
# on first initialization. This script only creates the application role and
6+
# grants it the rights needed to use that database.
7+
8+
: "${POSTGRES_DB:?missing POSTGRES_DB}"
9+
: "${POSTGRES_USER:?missing POSTGRES_USER}"
10+
: "${POSTGRES_APP_USER:?missing POSTGRES_APP_USER}"
11+
: "${POSTGRES_APP_PASSWORD:?missing POSTGRES_APP_PASSWORD}"
12+
13+
sql_escape_literal() {
14+
printf "%s" "$1" | sed "s/'/''/g"
15+
}
16+
17+
sql_escape_ident() {
18+
printf "%s" "$1" | sed 's/"/""/g'
19+
}
20+
21+
APP_USER_LIT=$(sql_escape_literal "$POSTGRES_APP_USER")
22+
APP_USER_IDENT=$(sql_escape_ident "$POSTGRES_APP_USER")
23+
APP_PASSWORD_LIT=$(sql_escape_literal "$POSTGRES_APP_PASSWORD")
24+
DB_NAME_IDENT=$(sql_escape_ident "$POSTGRES_DB")
25+
26+
# Check whether the application role already exists.
27+
role_exists=$(psql -tAc "SELECT 1 FROM pg_roles WHERE rolname = '$APP_USER_LIT'" \
28+
--username "$POSTGRES_USER" \
29+
--dbname "$POSTGRES_DB" | tr -d '[:space:]')
30+
31+
if [ -z "$role_exists" ]; then
32+
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" -v ON_ERROR_STOP=1 <<EOF
33+
CREATE ROLE "$APP_USER_IDENT" LOGIN PASSWORD '$APP_PASSWORD_LIT';
34+
EOF
35+
fi
36+
37+
# Grant access on the existing database created by initdb.
38+
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" -v ON_ERROR_STOP=1 <<EOF
39+
GRANT CONNECT ON DATABASE "$DB_NAME_IDENT" TO "$APP_USER_IDENT";
40+
GRANT USAGE, CREATE ON SCHEMA public TO "$APP_USER_IDENT";
41+
ALTER DEFAULT PRIVILEGES IN SCHEMA public
42+
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO "$APP_USER_IDENT";
43+
ALTER DEFAULT PRIVILEGES IN SCHEMA public
44+
GRANT USAGE, SELECT ON SEQUENCES TO "$APP_USER_IDENT";
45+
EOF

backend/db/migrations/tmp

Whitespace-only changes.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const dbName = process.env.MONGO_INITDB_DATABASE || 'efes-db';
2+
const appUser = process.env.MONGO_APP_USER;
3+
const appPassword = process.env.MONGO_APP_PASSWORD;
4+
5+
if (!appUser) {
6+
throw new Error('missing MONGO_APP_USER');
7+
}
8+
9+
if (!appPassword) {
10+
throw new Error('missing MONGO_APP_PASSWORD');
11+
}
12+
13+
db = db.getSiblingDB(dbName);
14+
15+
db.createUser({
16+
user: appUser,
17+
pwd: appPassword,
18+
roles: [
19+
{ role: 'readWrite', db: dbName }
20+
]
21+
});

backend/src/config/postgres.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { Pool } from 'pg';
22

33
const pool = new Pool({
4-
host: process.env.POSTGRES_HOST || 'localhost',
4+
host: process.env.POSTGRES_HOST || 'localhost',
55
port: Number(process.env.POSTGRES_PORT) || 5432,
66
database: process.env.POSTGRES_DB,
7-
user: process.env.POSTGRES_USER,
8-
password: process.env.POSTGRES_PASSWORD,
7+
user: process.env.POSTGRES_APP_USER,
8+
password: process.env.POSTGRES_APP_PASSWORD,
99
max: 10,
1010
idleTimeoutMillis: 30000,
1111
connectionTimeoutMillis: 2000,

docker-compose.yml

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ services:
66
dockerfile: Dockerfile
77
target: production
88
ports:
9-
- "${DOCKER_PORT_FRONTEND}:80"
9+
- "${DOCKER_PORT_FRONTEND:-4200}:80"
1010
depends_on:
1111
- backend
1212
networks:
@@ -22,7 +22,7 @@ services:
2222
dockerfile: Dockerfile
2323
target: development
2424
ports:
25-
- "${DOCKER_PORT_FRONTEND}:4200"
25+
- "${DOCKER_PORT_FRONTEND:-4200}:4200"
2626
volumes:
2727
- ./frontend/src:/app/src
2828
- ./frontend/public:/app/public
@@ -40,13 +40,17 @@ services:
4040
dockerfile: Dockerfile
4141
target: production
4242
ports:
43-
- "${DOCKER_PORT_BACKEND}:3000"
43+
- "${DOCKER_PORT_BACKEND:-3000}:3000"
4444
env_file: .env
45+
environment:
46+
MONGO_URI: mongodb://${MONGO_APP_USER}:${MONGO_APP_PASSWORD}@mongo:27017/${MONGO_DB}?authSource=${MONGO_DB}
4547
depends_on:
4648
postgres:
4749
condition: service_healthy
4850
mongo:
4951
condition: service_healthy
52+
flyway:
53+
condition: service_completed_successfully
5054
networks:
5155
- app-network
5256
profiles:
@@ -60,16 +64,20 @@ services:
6064
dockerfile: Dockerfile
6165
target: development
6266
ports:
63-
- "${DOCKER_PORT_BACKEND}:3000"
67+
- "${DOCKER_PORT_BACKEND:-3000}:3000"
6468
env_file: .env
69+
environment:
70+
MONGO_URI: mongodb://${MONGO_APP_USER}:${MONGO_APP_PASSWORD}@mongo:27017/${MONGO_DB}?authSource=${MONGO_DB}
6571
volumes:
6672
- ./backend:/app
67-
- backend_node_modules:/app/node_modules
73+
- backend-node-modules:/app/node_modules
6874
depends_on:
6975
postgres:
7076
condition: service_healthy
7177
mongo:
7278
condition: service_healthy
79+
flyway:
80+
condition: service_completed_successfully
7381
networks:
7482
- app-network
7583
profiles:
@@ -78,31 +86,37 @@ services:
7886
postgres:
7987
container_name: efes-postgres
8088
image: postgres:16-alpine
81-
ports:
82-
- "${DOCKER_PORT_POSTGRES}:5432"
8389
environment:
84-
POSTGRES_DB: ${POSTGRES_DB}
85-
POSTGRES_USER: ${POSTGRES_USER}
86-
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
90+
POSTGRES_DB: ${POSTGRES_DB}
91+
POSTGRES_USER: ${POSTGRES_ROOT_USER}
92+
POSTGRES_PASSWORD: ${POSTGRES_ROOT_PASSWORD}
93+
POSTGRES_APP_USER: ${POSTGRES_APP_USER}
94+
POSTGRES_APP_PASSWORD: ${POSTGRES_APP_PASSWORD}
8795
volumes:
8896
- postgres-data:/var/lib/postgresql/data
97+
- ./backend/db/init:/docker-entrypoint-initdb.d
8998
networks:
9099
- app-network
91100
restart: unless-stopped
92101
healthcheck:
93-
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
102+
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_ROOT_USER} -d ${POSTGRES_DB}"]
94103
interval: 5s
95104
timeout: 5s
96105
retries: 5
97106

98107
mongo:
99108
container_name: efes-mongo
100109
image: mongo:7
101-
ports:
102-
- "${DOCKER_PORT_MONGO}:27017"
110+
environment:
111+
MONGO_INITDB_DATABASE: ${MONGO_DB}
112+
MONGO_INITDB_ROOT_USERNAME: ${MONGO_ROOT_USER}
113+
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD}
114+
MONGO_APP_USER: ${MONGO_APP_USER}
115+
MONGO_APP_PASSWORD: ${MONGO_APP_PASSWORD}
103116
volumes:
104117
- mongodb-data:/data/db
105118
- mongodb-configdb:/data/configdb
119+
- ./backend/db/mongo:/docker-entrypoint-initdb.d
106120
networks:
107121
- app-network
108122
restart: unless-stopped
@@ -112,6 +126,23 @@ services:
112126
timeout: 5s
113127
retries: 5
114128

129+
flyway:
130+
image: flyway/flyway:latest
131+
container_name: efes-flyway
132+
command: migrate
133+
environment:
134+
FLYWAY_URL: jdbc:postgresql://${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/${POSTGRES_DB}
135+
FLYWAY_USER: ${POSTGRES_ROOT_USER}
136+
FLYWAY_PASSWORD: ${POSTGRES_ROOT_PASSWORD}
137+
FLYWAY_LOCATIONS: filesystem:/flyway/sql
138+
volumes:
139+
- ./backend/db/migrations:/flyway/sql
140+
depends_on:
141+
postgres:
142+
condition: service_healthy
143+
networks:
144+
- app-network
145+
115146
networks:
116147
app-network:
117148
driver: bridge
@@ -120,4 +151,4 @@ volumes:
120151
mongodb-data:
121152
mongodb-configdb:
122153
postgres-data:
123-
backend_node_modules:
154+
backend-node-modules:

0 commit comments

Comments
 (0)