Update a Docker site
A site run with Docker Compose can be updated in two ways: with Update now in the admin panel, or by building a new image and starting it. Either is safe if /app/data is a proper volume, which is the one thing to check first. This page is for whoever runs the server.
Check the data volume first
The image keeps everything it needs to remember in /app/data: the SQLite database and uploaded files if you use them, the copies of the database made before each update, and any release the site has downloaded for itself. The Dockerfile declares VOLUME ["/app/data"], but only a named volume in your compose file keeps that data across a new container.
| Stack | What holds /app/data |
|---|---|
docker-compose.sqlite.yml (one container, SQLite) | A named volume data. Nothing to change. |
docker-compose.yml (PostgreSQL and MinIO) | Nothing named. Docker makes an unnamed (anonymous) volume. Compose carries it over when docker compose up -d recreates the container, but a new container made after docker compose down gets a fresh, empty one. |
In the PostgreSQL stack your database and files are safe, since they are in PostgreSQL and MinIO. What is lost when the container is removed and made again (for example docker compose down then up) is a release downloaded with Update now. The site then quietly returns to the version in the image.
If you want one-click updates on that stack, add the volume to the app service and declare it:
services:
app:
# ...the rest of the service as it is...
volumes:
- data:/app/data
volumes:
data:
db-data:
minio-data:
caddy-data:
caddy-config:
The other way is to set UPDATE_MODE=notify in .env and always update with a new image, as below. Do that if you run more than one copy of the app service. See How updates work. The install pages explain the stacks: Docker Compose: PostgreSQL and MinIO and Docker: HTTPS, ports and volumes.
The volume's real name is the Compose project name, then an underscore, then data. By default the project name is the name of the folder holding the compose file, so a folder choirmaster gives choirmaster_data. List them with docker volume ls.
Way 1: Update now in the admin panel
This needs LICENSE_KEY in .env and nothing else. The image starts the site through the launcher, so the default mode is self.
- Take a backup of the data volume first: see Back up a single-server site.
- Follow Update your site with one click.
What happens inside the container: the release is unpacked into /app/data/releases/<version>/, a copy of the SQLite database is made in /app/data/backups/, and the launcher restarts the server from the new release. The container keeps running throughout; Docker does not see a restart. The container's health check (GET /api/health, every 30 seconds) may fail for a few seconds.
Because the release is in the volume, it survives docker compose up -d, restart and down. It is used until the image is as new or newer. After you build a newer image (way 2), the image's code takes over and the downloaded release is ignored.
Way 2: a new image
Use this when you prefer to deploy updates yourself, run the PostgreSQL stack without a named volume, or set UPDATE_MODE=notify.
-
Update the folder you build from to the new version. It is the folder that holds the Dockerfile and the compose files. The release download (the
.tar.gz) does not contain a Dockerfile, so keep to the source you built the image from. -
Rebuild and start. For the SQLite stack:
docker compose -f docker-compose.sqlite.yml up -d --buildFor the PostgreSQL stack:
docker compose up -d --build -
Check the version:
curl -s http://localhost:8080/api/healthUse your own
HTTP_PORT(default 8080). The answer shows the newversion.
Compose recreates the app container with the new image and keeps its named volume. The database schema is brought up to date every time the server starts (DB_AUTO_MIGRATE, on by default), and the schema files only ever create what is missing, so there is no separate migration step. If you set DB_AUTO_MIGRATE=false, run docker compose exec app npm run db:migrate after starting.
If you deploy to a Docker host over SSH with scripts/deploy.sh from the source repository, it copies a commit, rebuilds, restarts and waits for the health check. If the site has since updated itself to a newer version than the commit you deploy, it keeps running the newer version, because the highest version wins.
If the new version misbehaves
-
A version installed with the button that fails to start is rolled back by the launcher by itself, within 90 seconds. See When an update fails.
-
A version that starts and then misbehaves is not rolled back. If it came from the button, delete the downloaded releases and restart the container, which returns the site to the image's version:
docker compose exec app rm -rf /app/data/releasesdocker compose restart app -
If it came from a new image, build and start the previous version again. Schema changes only add tables and indexes, so an older version keeps working on a newer database.