uVersion
English
Download →

Wiki

Deploy with Docker

The uVersion server + PostgreSQL as containers, turnkey: download the image, load it, configure .env, start. Data and database location configurable.

An alternative to the .deb package: the uVersion server and its PostgreSQL database run as containers, brought up by a single docker compose up. Ideal if you prefer a containerized deployment, or to test quickly.

Prerequisites: Docker Engine 20.10+ with the Compose v2 plugin (docker compose version), and a uVersion licence key. The server refuses to start without a valid, signed key: get it from your account (or obtain one from the signup page).

Podman works too Replace docker with podman in every command on this page: podman compose pull, podman load -i, podman compose up -d. The docker-compose.yml provided already labels its mounts for SELinux (the access control enabled by default on Red Hat, Rocky, Alma and Fedora), with the :z suffix. Without this label, the container is denied access to the mounted directories, and the message looks like a corrupt database rather than a permissions problem.

Install

1. Fetch the compose file and the environment template

Two files, in a fresh folder that will become the stack's: the docker-compose.yml and the .env template.

mkdir uversion && cd uversion
wget -N -O docker-compose.yml https://uversion.io/downloads/server/docker/docker-compose.yml
wget -N -O .env https://uversion.io/downloads/server/docker/env.example

2. Fetch the image

Online, the registry is enough:

docker compose pull

With no Internet access (isolated network), download the archive and load it instead: it carries the same image name as the compose, so startup finds it without downloading anything.

wget -N https://uversion.io/downloads/server/docker/uversion-server_latest_docker.tar.gz
docker load -i uversion-server_latest_docker.tar.gz

3. Fill in .env

Two values are required: UVERSION_LICENCE__KEY (your licence key, the long signed string received by email) and POSTGRES_PASSWORD (a strong password for the bundled database).

nano .env

The secret that signs sessions is generated on first start and kept in the data directory. You have nothing to enter. It is part of the backup: if it disappears, everyone will have to sign in once again, nothing is lost. If you prefer to set your own (32 characters minimum), fill in UVERSION_SECURITY__JWT_SECRET in .env: it is indeed passed to the container, which then uses it instead of generating one.

Choose this password once and for all POSTGRES_PASSWORD is only applied when the database is created, while its directory is empty. Changing it afterwards in .env changes nothing on the PostgreSQL side, which keeps the old one, while the server presents the new one: the connection breaks without the message explaining it. To change it for real, change it on both sides:
# Changer reellement le mot de passe de la base
docker compose exec db psql -U uversion -c "ALTER USER uversion PASSWORD 'nouveau-mot-de-passe'"

# Puis reporter la MEME valeur dans POSTGRES_PASSWORD (.env) et relancer
docker compose up -d
Back up .env along with your data It contains the licence key and the database password, and it lives next to the docker-compose.yml, not in the volumes. Losing the machine while keeping the data disks leaves a database nobody can address and a licence to request again: the activation code received at signup is single-use and already consumed, only the licence key it produced is reusable, and that is the one that is here. Treat this file as a secret: chmod 600, and in the same backup as the database and the chunks (the pieces of files, which take up the space).

4. Start the stack

docker compose up -d

The server is then reachable at https://YOUR-HOST:8443. No domain or Let’s Encrypt certificate is required: the TOFU model handles it, that is, trust on first contact, like SSH when it asks you to confirm a machine's fingerprint the first time.

5. Read the server fingerprint

It is rewritten in the logs at every start:

# L'empreinte du serveur, reecrite dans les journaux a chaque demarrage
docker compose logs server | grep -i "TLS fingerprint"

This is the server fingerprint (SHA-256 of its certificate): the server serves self-signed HTTPS on port 8443, and each client confirms it on the first connection. Share it with your team (see TLS fingerprint). Do not confuse it with a checksum, which is used to verify a downloaded file: this one identifies your server.

6. Read the initial admin password

Log in as admin with this password, then change it. The file is deleted as soon as you change it.

# Le mot de passe admin initial (le fichier disparait des que vous le changez)
docker compose exec server cat /data/initial-admin-password

To follow the startup live, docker compose logs -f server works, but do not put it in the middle of a sequence of commands: the -f follows the logs indefinitely and never returns (Ctrl + C to exit).

Data and database

All the server's state (chunk store, TLS certificate and therefore the fingerprint, server identity) lives in one host directory, and the PostgreSQL database in another. Both are set in .env:

# Dans .env : placer les donnees et la base sur un disque large et sauvegarde
UVERSION_DATA_DIR=/srv/uversion/data      # chunks, cert TLS, identite serveur
UVERSION_DB_DIR=/srv/uversion/db          # PostgreSQL fourni

For a real deployment, point UVERSION_DATA_DIR at a large, backed-up disk: the binary chunks of an Unreal project can be sizeable. The container adjusts the permissions of the mounted directory on its own (it starts as root just long enough to fix ownership, then drops to an unprivileged user), so you have nothing to chown beforehand.

Longer first start when SELinux is active The engine then relabels the mounted directories recursively, once. It is instant on a fresh deployment, but expect several minutes on an already-large repository. Subsequent starts do not pay this cost again.

To use an external PostgreSQL already in place, set UVERSION_DATABASE__URL in .env, then remove the db service and the depends_on block in docker-compose.yml.

# Dans .env : brancher un PostgreSQL existant
UVERSION_DATABASE__URL=postgres://user:motdepasse@db.interne:5432/uversion

Move the data and database

The simplest case is to set both variables before the first up: nothing exists yet, there is nothing to copy. If the stack is already running, you have to stop it and copy the directories by hand, preserving ownership.

1. Stop the stack

Nothing must write during the copy:

docker compose down

2. Copy the two directories

It is the -a that preserves ownership and permissions:

sudo rsync -a ./data/server/ /srv/uversion/data/
sudo rsync -a ./data/db/     /srv/uversion/db/

3. Point .env at the new location

# Dans .env, LES DEUX lignes, pas une seule
UVERSION_DATA_DIR=/srv/uversion/data
UVERSION_DB_DIR=/srv/uversion/db

4. Restart

docker compose up -d
Move both, or neither The database does not contain your files: it contains the metadata that designate the chunks (the pieces of files) by their fingerprint. Moving the data without the database, or the reverse, leaves a database that references unreachable chunks. And a UVERSION_DB_DIR pointing at an empty directory gives a blank database: PostgreSQL initializes it without flinching, and you end up with a full chunk store paired with a database that has no metadata at all.

Three path pitfalls, all encountered:

  • A relative path is resolved relative to the compose file, not relative to the directory from which you run the command. ./data/server always designates the neighbour of the docker-compose.yml.
  • A network share (NFS, CIFS, a Windows share) generally refuses the chown that the container performs at startup. The container then exits immediately, on a permissions error that mentions neither uVersion nor storage. Use a local disk or a block volume.
  • rsync -a is what preserves ownership and permissions. A copy made with cp without options, or from a file explorer, resets everything to the current account, and PostgreSQL then refuses to start on its own directory.

Operations

Back up: the database

It carries the metadata: files, revisions, users, locks.

docker compose exec db pg_dump -U uversion uversion > uversion-db.sql

Back up: the server data

This is the host directory designated by UVERSION_DATA_DIR. Both backups count: one without the other restores nothing.

# .env n'est pas charge dans votre shell, chargez-le pour reutiliser la variable
set -a; . ./.env; set +a
DATA_DIR="$UVERSION_DATA_DIR"
[ -n "$DATA_DIR" ] || DATA_DIR=./data/server      # valeur par defaut

tar czf uversion-data.tar.gz -C "$DATA_DIR" .

Update

The data and the database are not touched, the migrations apply at startup.

docker compose pull
docker compose up -d   # les migrations s'appliquent au demarrage

On an isolated network, load a newer archive instead:

wget -N https://uversion.io/downloads/server/docker/uversion-server_latest_docker.tar.gz
docker load -i uversion-server_latest_docker.tar.gz
docker compose up -d

Stop

docker compose stop stops the containers, docker compose down removes them. In both cases your data stays in place.

down -v erases nothing here The published compose uses no named volume: your data and your database are in the two host directories designated by .env. Neither down nor down -v touches them. To really start from scratch, you have to delete these directories yourself. This is important to know: the reflex, after an authentication failure, is to run down -v then up, and that loops back onto exactly the same error, with the same database and the same password.