Upgrading LicenseTrack¶
This guide covers a Docker Compose upgrade while keeping the existing database, uploaded documents, database backups, and Official Extension storage intact.
Automatic pre-upgrade snapshot
When a new version needs to change the database schema, LicenseTrack first copies the
database to pre-upgrade/ next to the database file (the newest three copies are kept).
This is a safety net, not a replacement for the full volume backup described below.
Do not delete the data volume
Do not run docker compose down -v during an upgrade unless you intentionally want to delete LicenseTrack data. The -v flag removes named volumes, including the /data volume that stores the SQLite database and uploaded documents.
What persists¶
The application container is replaceable. Persistent data lives under /data in the Docker volume mounted by Compose:
/data/licenses.db/data/storage//data/backups//data/plugins/
Startup runs Alembic migrations automatically, so an upgrade normally means replacing the application source/image and starting the container against the same /data volume.
Integrity migrations stop rather than silently discard data when they discover dangling relationships that cannot be repaired safely. If startup reports such an error, keep the pre-upgrade volume backup, do not repeatedly replace the database, and inspect the named table, column, and example row IDs before retrying the upgrade.
Before upgrading¶
Upgrading to 1.2.1¶
1.2.1 is a fix release for 1.2.0 and adds no database migrations. Follow the normal upgrade steps below. When upgrading from a release before 1.2.0, also follow the 1.2.0 guidance.
Native installs on systems with SELinux enforcing: see SELinux enforcing on 1.2.0 and earlier if you need to roll back to a 1.2.0 or earlier archive.
Upgrading to 1.2.0¶
When upgrading from 1.1.24, five migrations run through the normal startup process:
e6f7a8b9c0d2: widens the document category column to fit every category.a7c3e91b5d24: adds claims that prevent concurrent evidence transfers on pending orders.5f0abf342086: creates the PO line register and assigns line numbers to existing licenses and pending-order lines with a PO number. A converted license shares its source line's number when their normalized PO matches.2ce70b265de2: adds the per-user Portfolio overview visibility setting, enabled by default.ed0290d95ac3: corrects maintenance records with the signature of the old Add Maintenance pricing bug, where a coverage cost was stored as a unit price for a quantity greater than one. It also corrects the active maintenance cost mirrored on covered licenses.
Keep a full pre-upgrade backup of the data volume and .env. The automatic
pre-upgrade/ snapshot contains only the database; it does not include
documents or configuration. Rollback must restore the previous application
with its matching backup. An Alembic downgrade removes the new PO line
register, evidence claims and visibility setting, and does not undo the
maintenance price correction.
Review these changes before relying on the upgraded data or integrations:
- Historical PO line numbers are assigned in creation order per normalized PO number, ignoring case and repeated or surrounding whitespace. They may differ from the original purchasing system's line numbers; reconcile them before matching external records by PO number and line.
- Line Total is quantity × unit price. The deprecated
totalPoPriceAPI field is still accepted and returned but no longer used, and is scheduled for removal in 1.3.0. Use a manual PO total for a negotiated order value. - CSV import reads
Itemas a PO line number. If an existing file usesItemfor product text, map it to Software Description or rename it toDescription. Imports with a blank currency now require acknowledgement of the default currency shown in the preview. - Numeric API values must be plain decimals, without localized separators.
Unknown request fields are logged;
STRICT_REQUEST_FIELDS=truerejects them with 422. Integrations should send only documented fields. - Scripts writing through a browser session cookie must send
X-LicenseTrack-Request: 1, including on/api/auth/logout. The built-in browser client already sends this header. API-token and bearer requests are unaffected. - Customized
ALLOWED_UPLOAD_EXTENSIONSvalues must include.lic,.msgto accept these new document types. Native installations using the previous default list gain them automatically; customized lists are preserved. - Reverse-proxy deployments should configure
FORWARDED_ALLOW_IPSas described in the deployment guide so sign-in throttling uses the client address.
After upgrading, confirm /api/health reports 1.2.0, review historical PO
lines and corrected maintenance totals, and smoke-test CSV import/export,
procurement conversion and document downloads. If an older conversion reports
a missing invoice, upload it on the pending order and retry evidence transfer.
Upgrades from earlier versions must also follow the guidance below.
Upgrading to 1.1.24¶
This release adds five migrations. All of them add new columns and run through the normal startup migration process:
d1e2f3a4b5c6: the Renewable? flag and Type Description for Service and Other, on licenses and sourcing lines. Existing Service and Other records start as one-off purchases.a7b8c9d0e1f2: the manual PO total on pending orders.b2c3d4e5f6a8: per-currency high-value thresholds. An existing single threshold is copied once to the first admin's display currency (EUR when none is set). The old value is kept but no longer used.c4d5e6f7a8b0: the Public app URL setting, empty by default.d5e6f7a8b9c1: the supported license on maintenance sourcing lines.
Stored admin configuration is not changed: completeness requirements, email templates, and other settings keep their current values. Only new installations start with PO number, invoice number, and budget owner required.
Existing Service and Other records whose end date has passed are retired by the first daily job after the upgrade, unless you mark them Renewable? first. To review them beforehand, filter License Overview by type before upgrading.
OEM and freeware records that carry an end date keep it until they are next saved in the full Edit form, which then clears it. No migration rewrites them.
Keep a full pre-upgrade volume backup for rollback. Downgrading removes the new columns and their values: renewable flags, type descriptions, manual PO totals on pending orders, per-currency thresholds, the public app URL, and the supported license on maintenance sourcing lines.
After upgrading, set Public app URL in Admin > Settings > Notifications if notification emails should link to licenses, and review the high-value thresholds in Admin > Settings > Renewals.
Upgrading to 1.1.23¶
This release adds two migrations: one stores planned successor links between sourcing lines, and the other records claims on webhook deliveries so concurrent workers do not send the same delivery. Both run through the normal startup migration process. Keep a full pre-upgrade volume backup for rollback; a downgrade removes planned successor links that have not yet been converted.
Human bearer tokens created before the per-login session model that lack a recorded session are now rejected. If a browser or API client still holds one, sign in again. API tokens are unaffected.
Upgrading to 1.1.22¶
This patch includes migrations for non-reusable user IDs, maintenance coverage restoration when unlinking an existing successor, and independently revocable login sessions. They run through the normal startup migration process.
Existing browser sessions require a fresh sign-in after upgrading. Each login has its own sliding expiry; logging out or reaching the inactivity timeout revokes that session without signing out other devices. Browser tabs share activity, and panel scrolling counts as activity. Split API deployments retain in-memory bearer tokens and may require another sign-in after a page reload if the browser cannot send the API session cookie.
Downgrading the session migration removes login-session identities and also requires a fresh sign-in. Keep the full pre-upgrade volume backup for rollback.
From the current install directory, identify the volume mounted at /data:
cd /path/to/LicenseTrack
CID=$(docker compose ps -q license-lifecycle)
DATA_VOL=$(docker inspect "$CID" --format '{{range .Mounts}}{{if eq .Destination "/data"}}{{.Name}}{{end}}{{end}}')
echo "$DATA_VOL"
Back up the full volume:
mkdir -p ~/licensetrack-upgrade-backups
docker run --rm \
-v "$DATA_VOL":/data:ro \
-v "$HOME/licensetrack-upgrade-backups":/backup \
alpine \
sh -c 'tar -czf /backup/licensetrack-data-pre-upgrade-$(date +%Y%m%d-%H%M%S).tar.gz -C /data .'
Also keep a copy of .env:
Normal same-directory upgrade¶
The cleanest upgrade path is to keep the same install directory and Compose project name.
Replace the source files with the new release while keeping the same folder name and .env. Then rebuild and start:
Check health and logs:
docker compose ps
docker compose logs --tail=100 license-lifecycle
curl http://localhost:8080/api/health
The health response should include the expected version:
Log in and smoke-test license listing, document downloads, settings, backup listing, and any configured SMTP/OIDC integrations.
If the install folder changes¶
Docker Compose names volumes from the Compose project name. By default, the project name comes from the folder name. For example:
~/LicenseTrack-1.0.4 -> licensetrack-104_license_lifecycle_data
~/LicenseTrack -> licensetrack_license_lifecycle_data
If you move to a different folder name, Compose may create a new empty volume. The symptom is a startup log that runs every migration from initial_schema, and the app opens with an empty database.
To reuse an existing volume after changing folder/project name, mark the volume as external in docker-compose.yml:
Use the volume name you found before upgrading, for example:
Then start again:
Podman upgrades¶
If you run LicenseTrack with plain podman run, upgrade by replacing the container while reusing the same /data volume.
Identify the current /data volume:
podman inspect licensetrack --format '{{range .Mounts}}{{if eq .Destination "/data"}}{{.Name}}{{end}}{{end}}'
Back up the volume:
mkdir -p ~/licensetrack-upgrade-backups
podman run --rm \
-v license_lifecycle_data:/data:ro \
-v "$HOME/licensetrack-upgrade-backups":/backup \
alpine \
sh -c 'tar -czf /backup/licensetrack-data-pre-upgrade-$(date +%Y%m%d-%H%M%S).tar.gz -C /data .'
Stop and remove only the old container, not the volume:
Build the new image from the release source:
Start the new container with the same volume mounted at /data:
podman run -d --name licensetrack -p 8080:8000 \
--env-file .env \
-v license_lifecycle_data:/data \
license-lifecycle-system:1.2.1
Check health:
If your Podman volume has a different name, substitute it in both -v arguments. Do not use podman volume rm during an upgrade unless you intentionally want to delete persistent data.
If you use podman compose, follow the Docker Compose guidance above. Compose project-name behavior can still change generated volume names when the install folder changes.
Stable project names¶
For new installs, you can avoid folder-name surprises by setting a stable Compose project name before first startup:
Do not add or change COMPOSE_PROJECT_NAME on an existing install unless you also confirm which data volume will be used. Changing it later changes the Compose-generated volume name.
Recovery if an empty database appears¶
If the upgraded container starts with an empty database:
- Stop the new container with
docker compose down. - Do not delete any Docker volumes.
- List available volumes with
docker volume ls. - Inspect old containers to find their
/datavolume:
- Update
docker-compose.ymlto use the correct existing volume as an external volume. - Start again and confirm that the startup logs no longer run the full migration chain from the initial schema.
After upgrading¶
- Confirm
/api/healthreports the expected version. - Confirm the container is healthy and not restarting.
- Review logs for migration errors.
- Log in with an admin account.
- Smoke-test license listing, document access, settings, backup listing, and configured integrations.
- Confirm scheduled database backup and notification settings remain as expected.
When upgrading from 1.1.0 or earlier, a browser may still hold the old SPA shell
under its previous cache policy. If /api/health reports the new version but
the interface still looks or behaves like the old release, perform one hard
refresh or clear that site's cached files. Releases from 1.1.1 onward require
the SPA shell to revalidate, so this should be a one-time transition.