GitLab can build on the VPS or on a runner. Select the strategy in Deployment → Defaults; it determines the generated pipeline. Application source, local database data and server data have separate update workflows.
1. Configure the generator
Select CI provider: GitLab, choose Release branch, and keep Automatic deploy off for the first release. The default release branch is main. If you use production, enter that name and protect that branch in GitLab.
In Deployment → Environments, use + Create to add a profile such as production. CI selects it with ADMINGEN_ENVIRONMENT, defaulting to production. A profile name and a Git branch are different settings.
After Save → Generate, commit .gitlab-ci.yml, .gitlab/admingen.yml, the model, generated profile and its custom overlay. Additional jobs belong in deploy/custom/gitlab.yml.
2. Choose the build location
| Mode | Build location | Jobs and delivery |
|---|---|---|
| Source / Build on server | One VPS | admingen-deploy builds and deploys the commit; CI does not upload an image archive |
| Archive | GitLab runner | admingen-build, then deployment transfers release JSON and images.tar |
| Registry | GitLab runner | Build publishes images to a registry; deploy receives their release metadata |
Prepare Source on the server
Select Build on server and set Source repository on server, for example /home/deploy/projects/PROJECT_NAME. This is an existing Git checkout, separate from Runtime directory. Select Single server and Source in the profile and set Backend SSH target, for example deploy@YOUR_SERVER_HOST.
The VPS needs Docker/Compose, Git, Bash, jq and flock. Prepare an owned writable runtime root and clone this GitLab project into the configured source directory. Its origin must point to that project without embedded credentials. Give the server a read-only GitLab Deploy key, run git fetch origin as the deployment user once, and resolve access/host prompts beforehand.
The server's GitLab read key and the key used by CI to enter the VPS serve different connections. Configuring one does not configure the other.
Prepare Archive or Registry runners
These modes use a privileged Docker executor with Docker-in-Docker, TLS, shared /certs/client and a /builds checkout visible to both the job and DinD. Source uses a lightweight SSH job without DinD. Your registry may be GitLab, GHCR, Docker Hub or a custom service.
3. Configure variables
Set protected GitLab File variables:
| Variable | Value |
|---|---|
ADMINGEN_SSH_KEY |
Private key allowing CI to enter the VPS |
ADMINGEN_KNOWN_HOSTS |
Trusted known_hosts entries for the servers |
Ordinary profiles do not require a full JSON variable: CI reads generated settings and the committed custom overlay. Optional ADMINGEN_ENVIRONMENT_JSON completely overrides that configuration. Remove an old Archive override when switching to Source. Source does not use ADMINGEN_BUILD_ENVIRONMENT_JSON.
Registry supports ADMINGEN_BUILD_REGISTRY_USER/PASSWORD for publication and ADMINGEN_REGISTRY_USER/PASSWORD for deployment. Archive and Source require no registry credentials. Keep secrets out of models, profile JSON, screenshots and Git.
ADMINGEN_RELEASE_BRANCH, ADMINGEN_AUTO_DEPLOY, ADMINGEN_ENVIRONMENT and ADMINGEN_SOURCE_DIRECTORY can explicitly override CI settings/selection. Leave them unset to use the generated defaults. Variable scope must grant the selected CI environment access; scheduled maintenance can use a separate environment.
4. First installation
Complete Server and domain preparation. With external ingress, host Nginx and HTTPS must already be configured; the generated frontend uses a dedicated loopback port.
For Source, run admingen-provision in the protected release-branch pipeline, wait for success, then run admingen-deploy. Each job builds its own candidate on the VPS. First administrator bootstrap and initial data import are separate actions. An established application only needs Deploy.
For Archive/Registry, wait for admingen-build. Initial provision and deploy use that artifact. Do not repeat provision for ordinary updates.
5. Routine updates
Change the model and custom code locally, Save → Generate, apply local migrations and commit the files. Merge into the selected release branch and run admingen-deploy.
Source CI obtains the exact pipeline SHA in a separate checkout under RUNTIME_ROOT/ci-source/worktrees/, without resetting the operator's checkout. It checks the release-branch tip, builds images, runs the ordinary controller and verifies readiness. Source jobs using the same Unix user share a host-wide lock, serializing builds across repositories. That does not combine project databases.
The old release runs during build. Installation has a short interruption while the controller stops writes, performs the configured backup/migration steps and starts the new code. Server records, accounts and files remain; routine deploy does not reimport local data. A build failure leaves the running release unchanged. If deployment has already started and fails, inspect operation state before retrying.
After a successful first release, enable Automatic deploy in UI if desired, Save/Generate and publish the change. Configured GitLab approvals still apply.
6. Maintenance and recovery
Manual admingen-stop stops the complete stack, including PostgreSQL. It is not a routine update step.
Schedules use the protected release branch and ADMINGEN_OPERATION=maintenance, backup or certificates. With backup None, maintenance skips the copy; an explicit backup fails. With external ingress, the host Nginx operator maintains its certificates.
| Situation | Check |
|---|---|
| No Deploy job | Selected protected Release branch, available runner and variable access |
| Missing profile | Generated/custom profile is actually committed to the release branch |
| Source CI expects Archive | Default strategy differs, or a full JSON variable still overrides the profile |
| Permission denied (publickey) | Identify which connection failed: CI → VPS or VPS → GitLab |
| Obsolete pipeline | Run the pipeline for the current release-branch commit |
| Interrupted bootstrap/reservation | Inspect the recorded operation; do not delete state manually |
Source archives remain on the VPS for the controller. Runner-built Archive/Registry artifacts expire after 90 days. Check disk usage periodically.