Local Docker Compose Stack¶
Local development uses Docker only for infrastructure dependencies. The backend (Go) and frontend (Expo) run on the host so that hot reload and IDE tooling work without container overhead.
The stack is defined in docker-compose.dev.yml at the repository root.
Services¶
| Service | Image | Port(s) | Purpose | Profile |
|---|---|---|---|---|
postgres |
kartoza/postgis:15-3.3 |
5432 |
Primary database (Postgres 15 + PostGIS 3.3) | default |
redis |
redis:7-alpine |
6379 |
Cache, queues, presence, pub/sub | default |
minio |
minio/minio:latest |
9000, 9001 |
S3-compatible object storage (9001 is the web console) |
default |
photon |
rtuszik/photon-docker:2.2.0 |
2322 |
Self-hosted geocoder | default |
pgadmin |
dpage/pgadmin4:latest |
5050 |
Postgres web UI — login admin@local.dev / admin |
tools |
redis-commander |
rediscommander/redis-commander:latest |
8081 |
Redis web UI | tools |
Default credentials for the local stack (intentionally weak — they never leave your machine):
- Postgres:
tomoda/tomoda123, databasetomoda - MinIO: root user
tomoda, root passwordtomoda123
Redis Commander port clash
redis-commander exposes port 8081, which is the same port the Expo dev server uses for the web frontend. If you run both at once you'll see a bind conflict. Either skip the tools profile or remap one of the ports.
Starting the stack¶
The repo ships two entry points:
task dev (recommended)¶
This is what you run day to day. It:
- Brings up only
postgres,redis,minio, andphotonfrom the dev compose file (notoolsprofile, no application containers). - Waits a few seconds, then runs database migrations (
task db:migrate). - Regenerates Swagger docs (
task gen:docs). - Attempts to pull secrets from GCP Secret Manager (see Secrets). If
gcloudis not authenticated, falls back to hard-coded local defaults. - Starts the backend with Air for hot reload (
backend && air) on:8080.
You then start the frontend in a second terminal:
task dev:frontend
Just the infra services¶
If you want to run the backend yourself (e.g. attach a debugger) but still need Postgres/Redis/etc, you can bring up just the dependencies:
docker-compose -f docker-compose.dev.yml up -d postgres redis minio photon
This is what task dev does internally before invoking Air.
Including the GUI tools¶
docker-compose -f docker-compose.dev.yml --profile tools up -d
This brings up pgAdmin (http://localhost:5050) and Redis Commander (http://localhost:8081).
Volumes & persistence¶
Each stateful service has a named volume that survives docker-compose down:
postgres_tomoda_data→/var/lib/postgresql/dataredis_tomoda_data→/var/lib/redisminio_tomoda_data→/dataphoton_data→/photon/data(Photon index — the first download takes several minutes)
To wipe everything and start fresh — most commonly to reset a corrupted database or test migrations from scratch:
task docker:clean # docker-compose down -v --remove-orphans
task docker:up # bring everything back up
task db:migrate # re-run migrations on the empty database
task db:test:setup # (optional) re-seed test users / events
Or in one shot:
task db:reset
Network¶
All services share the tomoda-network bridge network so they can talk to each other by service name. The host reaches them through the published ports listed above (localhost:5432, localhost:6379, etc.).
Photon first-run¶
The Photon container downloads its geocoding index on first start. The default REGION=canada (smallest index — overridable via the PHOTON_REGION env var) takes a few minutes. The healthcheck has a 300-second start_period to account for this. See Photon for how to switch to the multilingual GCS-hosted index for testing Japanese/Chinese queries.
Health checks¶
Every service defines a Compose-level healthcheck. The most useful one in practice:
docker-compose -f docker-compose.dev.yml ps
A service stuck on (health: starting) for more than a few minutes (other than photon on first launch) usually means something is wrong — check docker-compose logs <service>.