Setting up a Local Development Environment¶
What you are running¶
The stack is four containers:
| Service | Role |
|---|---|
db |
PostGIS 16, holds sites, observations and the admin_countries layer |
django |
Django 4.2 under uwsgi, plus the built React frontend |
tiles |
pg_tileserv, publishes observations and sites as vector tiles |
web |
nginx, the only service you talk to directly (port 61122) |
django speaks the uwsgi protocol rather than HTTP, so it cannot be browsed
directly. Always go through nginx.
Prerequisites¶
- Docker with Compose v2, so
docker compose(notdocker-compose). Linux, macOS and Windows with WSL2 are all fine. - About 8 GB of free disk. The Django image bundles TensorFlow.
- An editor of your choice.
Apple Silicon and other arm64 hosts
The Django image must be built for linux/amd64, which the override template
below does for you. A native arm64 build fails: geopandas depends on
fiona, which publishes no aarch64 wheels, so pip compiles it from source,
and deployment/docker/Dockerfile installs gcc without g++. Even with
g++ present, Debian bullseye only ships GDAL 3.2.2 while current fiona
requires GDAL 3.4 or newer. Emulation is slower to build but matches the
production runtime exactly.
Installation steps¶
-
Clone the repository.
-
Create your environment file.
Then edit
.env. Every variable is documented inline. The ones you must set:Variable Why POSTGRES_PASSdatabase password SECRET_KEYleaving it blank falls back to a value committed in the repository DJANGO_SUPERUSER_USERNAME,_EMAIL,_PASSWORDthe admin account; creation is skipped if any is blank MINIO_*/AWS_*object storage for photos and the classifier model -
Create the compose override.
This step is easy to miss and nothing works without it. The
MakefilesetsCOMPOSE_FILE=docker-compose.yml:docker-compose.override.yml, so every make target fails until the file exists. The override also supplies the development nginx configuration and thelinux/amd64platform. -
Build the images.
-
Start the stack.
No manual wait is needed:
dbhas a healthcheck anddjangodepends on it. The first start also runsnpm install && npm run buildbecauseDEV_SETUP=TRUE, which takes a few minutes. -
Open the application at http://localhost:61122/.
-
Speed up later restarts. Once
django_project/minisass_frontend/src/distexists, setDEV_SETUP=FALSEin.envanddocker compose up -d django.
Frontend development¶
Two mutually exclusive modes, both driven by DEBUG:
DEBUG=Falseserves the built bundle from/static/. Use this for backend work and for anything resembling production.-
DEBUG=Truemakes the page load the Vite dev server instead, giving hot module reloading. The page stays blank until you also start Vite:
Forgetting the second half of that is the usual cause of an apparently empty page.
Vector tiles¶
pg_tileserv publishes public.minisass_observations, public.sites and
public.admin_countries. Check what is available:
The MapLibre style is served by this deployment from
django_project/webmapping/styles/, collected to
/static/webmapping/minisass_style_v1.json. Edit the file in the repository and
re-run collectstatic to change the map's appearance.
Image classifier¶
Observation photos are classified into macroinvertebrate groups by a Keras model
held in object storage at <MINIO_BUCKET>/ai_image_calculation.h5. It loads on
first use, so warm it rather than making a user's upload wait:
If the model cannot be fetched, classification is skipped and observations still save normally.
Troubleshooting¶
Blank page¶
Almost always one of:
DEBUG=Truewithout the Vite dev server running. Either setDEBUG=Falseor runmake frontend-dev.-
The frontend was never built. Set
DEV_SETUP=TRUEand restartdjango, or build it by hand:
Site loads but the map is empty¶
Check that the tile service is reachable and returning data:
An empty database produces valid but empty tiles, which is expected.
Images are missing¶
Photo URLs are rendered as /minio-media/<key>. nginx serves that path, either
from local MinIO or by proxying your S3 bucket. A 403 from S3 usually means the
object does not exist, because anonymous requests without s3:ListBucket
permission get AccessDenied rather than 404.