Skip to content

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.