Updating¶
GeoDeploy checks for new versions and can update itself from the dashboard. Everything lives under Settings ▸ Infrastructure.
Update from the dashboard¶
The Updates card shows the version you are running, what is available, and which version to install. Pick one, then press the button: it fetches the new code, rebuilds what changed, restarts the affected services, health-checks the result and rolls back automatically if the new version does not come up healthy.
No swap? Add some before updating
An update builds the dashboard, and building needs far more memory than running does. With no swap the kernel kills the build part-way through — the update appears to hang, and you are left with stopped containers and no new image.
Run free -m. If the Swap row is 0 — which is how most cloud images ship, at any size — add
some. One command, once, and the build completes instead of being killed (it does not
noticeably slow down: what gets paged out is idle build memory):
sudo fallocate -l 2G /swapfile && sudo chmod 600 /swapfile
sudo mkswap /swapfile && sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
If it has already happened, see Recovering from a failed update.
How the updater knows it worked
Coming up healthy is not the same as being new — an old container answers a health check perfectly. So after the health check the updater also verifies that the images were actually rebuilt (when their code changed) and that the services are running those images. If they are not, it reports an error and leaves the version marker on the OLD commit rather than claiming a version that is not running.
What survives an update
Your database, uploaded files, published portals and settings are all outside the application containers, so an update replaces the software and leaves your data alone.
Updates are additive to the schema: new columns are added if missing, nothing is dropped or renamed automatically.
Choosing a version¶
Four targets, and the card remembers which one this instance follows:
| Target | What it means | Choose it when |
|---|---|---|
| main (development) | The newest commit on the development branch | You want fixes and features as they land, and can tolerate the occasional rough edge |
| Latest release | The newest published, non-prerelease tag | You want the recommended, deliberately-cut version — the default for a production instance |
| A specific release | Any published tag, including an older one | Holding back on a version you have tested, or stepping back down after a bad update |
| Another branch (advanced) | Any branch on the repository | Trying an unreleased feature branch on a real instance before it merges |
An instance installed at a release stays on releases: it is judged against the newest release,
not against the development branch, so being many commits behind main is the normal, intended
state and the card does not nag about it.
A branch is not a version
Branches are work in progress: unreleased, possibly mid-refactor, and not something to leave a production instance on. Take a backup first and come back to Latest release when you are done. Going back is the same three clicks.
Stepping back is not free
Schema changes only ever add, so older code against a newer database generally runs. But data written by a feature that the older version does not have will be invisible to it, and a portal published by a newer runtime is not re-generated by going back. Take a backup first — see Backups and restore.
The Infrastructure panel¶
Pick a service on the left, then work with it through the tabs:
| Tab | What it gives you |
|---|---|
| Logs | Live output from that service, with adjustable history |
| Terminal | A shell inside the container, for the owner only (off until enabled under Environment) |
| Environment | Owner-only instance settings, applied per service — no terminal needed |
| Deployments | History of updates, with what changed and whether it succeeded |
You can also start, stop and restart individual services from here.
The terminal is a real shell
It runs as root inside the container and is restricted to the owner account, and it is off by default — switch it on under the Environment tab and apply. Use it for inspection; prefer the dashboard for anything it can already do.
Services¶
| Service | Role |
|---|---|
| api | The application: dashboard backend, uploads, publishing |
| celery | Background work: ingestion, tiling, exports, backups |
| postgres | The spatial database, and GeoDeploy's own state |
| minio | Object storage for files, rasters and portal assets |
| martin | Vector tiles from the database |
| titiler | Raster tiles from Cloud-Optimized GeoTIFFs |
| redis | The task queue |
| nginx | Routing and TLS |
| ui | The dashboard itself |
A red service is worth investigating before anything else — most "my layer will not draw" reports turn out to be a tile service that is not running.
Updating from the command line¶
If the dashboard is unreachable, run the same updater from the server:
cd ~/geodeploy
sudo bash installer/self-update.sh # latest development code
sudo bash installer/self-update.sh v1.0 # a specific release
sudo bash installer/self-update.sh feat/something # a branch
sudo bash installer/self-update.sh main # back to development
The argument takes a release tag, a branch or a commit — the same choice the dashboard offers,
and the same one GEODEPLOY_VERSION gives the installer:
curl -fsSL https://raw.githubusercontent.com/bravemaster3/geodeploy/main/installer/install.sh \
| GEODEPLOY_VERSION=v1.0 bash
Use the script, and run it from the install directory
Prefer installer/self-update.sh over git pull && docker compose up -d. A bare docker compose
up -d also targets the containers the setup wizard provisions outside Compose (postgres, minio,
titiler) and collides with them, and running Compose from anywhere but the install directory
(~/geodeploy by default) recreates containers against the wrong paths and detaches them from
your data. The script recreates only the code services, reloads nginx without downtime, and rolls
back if the result is unhealthy.
Recovering from a failed update¶
If an update died part-way — the build was killed, the server rebooted, the connection dropped — the instance is usually stopped, not damaged. Your data is in the database and object storage, neither of which an update touches. Work through this from the server:
cd ~/geodeploy
# 1. What is actually running? Include stopped containers.
docker ps -a --format 'table {{.Names}}\t{{.Status}}'
# 2. Was it memory? (A killed build says so here.)
free -h
sudo dmesg | grep -i -E 'out of memory|killed process' | tail
# 3. Start the services the setup wizard provisioned (these live OUTSIDE Compose).
docker start geodeploy-postgres geodeploy-minio geodeploy-redis geodeploy-martin geodeploy-titiler
# 4. Start the application services. --no-deps so Compose touches nothing else.
docker compose up -d --no-deps geodeploy-api geodeploy-ui celery nginx
# 5. Confirm.
curl -sf localhost/health && echo "healthy"
docker compose logs --tail=50 geodeploy-api
Never run a bare docker compose up -d here
PostgreSQL, MinIO and TiTiler are provisioned by the setup wizard outside Compose, with fixed
container names. A blanket up -d collides with them (container name /geodeploy-postgres is
already in use) and can detach services from your data. Always name the services, as above.
"It gets to the login page but will not log in" is the classic shape of this: nginx and the
dashboard are up (so you see the page), while the API or the database behind it is not. Step 3 and 4
fix it. Confirm with docker compose logs geodeploy-api — a database it cannot reach says so plainly.
If an image is genuinely missing because the build never finished, rebuild one at a time (the dashboard is the memory-hungry one) rather than letting both run at once:
docker compose build geodeploy-ui
docker compose build geodeploy-api
docker compose up -d --no-deps --force-recreate geodeploy-api geodeploy-ui celery
Before a big change¶
Take a backup first — it is a button, and restoring is also a button. See Backups and restore.