Skip to content

Maintenance and upgrades

Terminal window
docker compose pull
docker compose up -d

That is the whole routine. The image carries the server and the built client together; schema changes are applied on boot, and installed modules are on a volume the upgrade does not touch.

Pin a build when you want a deploy you can reproduce. IMAGE_TAG defaults to latest; every merge also publishes sha-<7>, so

Terminal window
IMAGE_TAG=sha-042a151 docker compose pull && docker compose up -d

deploys an exact build, and putting that value in .env makes it the one this host runs until you change it. Rolling back is the same command with the previous tag — with one caveat that decides whether it works.

Two volumes and one directory hold everything that cannot be re-downloaded: the database, the uploads, and ./modules.

  1. Dump the database. From the deployment directory, while the stack is up:

    Terminal window
    docker compose exec -T db sh -c \
    'mariadb-dump -u root -p"$MARIADB_ROOT_PASSWORD" --single-transaction --routines runic_gateway' \
    > backup-$(date +%F).sql

    --single-transaction is what makes it consistent without locking the site.

  2. Copy the uploads volume.

    Terminal window
    docker run --rm -v <deployment>_uploads:/from -v "$PWD":/to alpine \
    tar czf /to/uploads-$(date +%F).tgz -C /from .

    The volume is named after the directory Compose runs in — docker volume ls shows the exact names.

  3. Keep ./modules, ./brand and your two files. They are ordinary host directories; whatever backs up the rest of the host covers them.

Restoring the database is the same command inverted — mariadb … < backup.sql — into a stack whose image is the one the dump came from.

./logs/app.log on the host, because the Compose file bind-mounts it there. docker compose logs -f app shows the same stream live.

LOG_LEVEL sets console verbosity and FILE_LOG_LEVEL the file’s — the file keeps the fuller record on purpose. Nothing rotates them for you.

docker compose restart app is the ordinary restart, and it is what the admin panel’s Restart the server button amounts to. Restarts are needed after installing, enabling or uninstalling a module, and are harmless otherwise.

docker compose down stops everything and keeps the data. docker compose down -v also deletes the volumes — the database and every upload. There is no undo.

The app publishes port 3000 and binds all interfaces, so any proxy that can reach the host can serve it. Two settings make it correct rather than merely working, both covered in Install the site: TRUST_PROXY, so the address your rate limiting and IP bans act on is the visitor’s rather than the proxy’s, and COOKIE_SECURE=auto.

Three rules for whatever proxy you use:

  • Forward only 3000. INTERNAL_PORT (3001) is the server-to-bot channel and must never be reachable from outside; the Compose file deliberately does not publish it.
  • Deny /api/v1/internal at the proxy as well. Belt and braces: that route no longer rides the public listener, and an explicit deny costs nothing.
  • Terminate TLS at the proxy. The app speaks HTTP; it is not meant to hold a certificate.

A different deployment on a different host, and it moves on its own schedule:

Terminal window
sudo runicgateway update # re-resolves the bundle; --verify to see it first
sudo runicgateway doctor # confirm afterwards

update replaces the sidecar and restarts its service, re-syncs the overlay, and tells you when ServUO needs restarting — it never restarts your shard itself. Because it resolves a bundle, the sidecar and the plugin move together and cannot end up disagreeing about the protocol.