Build Projects
Build container images from Git repositories and publish them through Citadel.
Build Projects turn source from a Git repository into Docker images. A project defines the source, builder, image tags, and optional Registry destination; each run records its commit, output, and logs.
Use a Build Project for Dockerfile builds. Use Scripts for scripts that call Citadel APIs or coordinate operational tasks.
Build your first image
You need a synced repository, a Dockerfile, an online builder Platform, and a registry that can accept image pushes. Ask the application's maintainer for the build paths.
- Open Build Projects, select Add Build, and enter a name.
- Choose the repository and branch. For a repository with a Dockerfile at its
root, set Context to
.and Dockerfile toDockerfile. - Under Builder, select Docker platform and an online Platform. Check context limits before choosing a remote builder.
- Enable Push to registry, select the Registry, and enter an Image repository
such as
team/api. Use{branch}-{shortSha}as the tag template to identify the source revision. The Registry supplies the hostname. - Save, select Build, and open Runs. Wait for Succeeded, then check the resolved commit, image reference, digest if available, and run logs.
The image is now available in the Registry. A Build Project does not start an application until a Deployment or Stack uses its output.
Deploy the image
- Create or open a Deployment on a Docker Standalone Platform.
- Choose Build as its image source and select this Build Project. Configure the application's networks, ports, storage, and bindings.
- Leave Redeploy On Build disabled for the first deployment. Save, then select Deploy or Redeploy.
- Check the container's logs and application endpoint. Confirm the applied artifact matches the build you intended to run.
For Compose applications, use Stack Build Images bindings instead. The target host must be able to pull the published image with the configured Registry access.
Automate later builds
After the manual path works, enable a build webhook to build on relevant Git changes and Redeploy On Build on the intended consumers. These triggers require Automated Operations; external Build Pool execution also requires Elastic Build Execution.
Push a small application change and verify both the Build run and the resulting Deployment or Stack operation. Build success and deployment success are separate outcomes. Use this built-in flow when it fits; a script is only needed for additional coordination. A queued build response does not mean its image is ready to deploy.
License Availability
Community can:
- create build projects and build-pool definitions
- test and manage build-pool configuration
- run builds manually on existing Local, Agent, and Edge Agent platforms
- view build configuration, history, artifacts, and logs
- cancel in-flight builds
Team capabilities add:
Automated Operations: webhook-triggered builds and automatic deployment or stack apply after a successful buildElastic Build Execution: builds dispatched through an external Build Pool
A webhook-triggered build using an external Build Pool requires both
capabilities. A manual build using an external Build Pool requires only
Elastic Build Execution.
If a license changes, an external build that has already started may finish or
be cancelled. Queued external-pool builds do not lease or provision new builder
capacity without Elastic Build Execution.
Current Runner Support
Builds can run on Docker platforms connected through:
LocalAgentEdgeAgentBuild Pool
Local builds stream the build context from Citadel Core to the local Docker daemon.
Agent and Edge Agent builds package the committed context on Core and transfer it to the builder. The current archive step is limited to 12 MiB, before the 16 MiB transport envelope; see Build Context Size.
Build Pool builds run on a reusable build pool instead of a Docker platform. For a self-managed VM pool, Citadel either connects to a dedicated inbound Agent endpoint or uses a pool-scoped Edge Agent connection. In both modes, the builder host's Docker daemon performs the build and push.
Build Pool execution currently supports only self-managed VM pools using inbound or Edge Agents. AWS EC2 provisioning is not implemented; manually provisioned AWS VMs can use the self-managed provider. See Build Pools.
Executing a build through a Build Pool requires Elastic Build Execution.
Creating, testing, updating, disabling, or deleting the pool definition does
not.
Prerequisites
Connect the Git repository, an online Platform or Build Pool, and a Registry with push access. Prepare any stored secrets required by the Dockerfile. Keep Git credentials, Registry credentials, and Dockerfile secret mounts separate: they authorize different parts of the build.
Create A Build
Open:
Build Projects -> Add BuildSet:
- Name: unique name shown in lists, activity, and run history.
- Description: optional notes for other admins.
- Tags: optional filters for organizing build projects.
- Repository: Git repository that contains the Dockerfile.
- Branch: branch Citadel syncs before building.
- Context: directory relative to the repository root sent to Docker as the build context.
- Dockerfile: Dockerfile path relative to the repository root.
- Target stage: optional Dockerfile stage for multi-stage builds.
- Builder: Docker platform or Build Pool that runs the build.
- Push to registry: publish the output; disable only when an image local to the builder is sufficient.
- Registry: registry Citadel pushes tags to.
- Image repository: repository path under the selected registry, such as
team/api. - Tags: comma-separated tag templates.
- Enabled: whether runs can be queued.
- Timeout seconds: maximum build and push duration.
- Run retention: number of recent terminal runs to keep.
- Build arguments: optional Docker build args for non-sensitive values.
- Build secrets: optional mappings from Dockerfile BuildKit secret ids to Citadel secrets.
Save the build before queueing a run.
Duplicate configuration
Choose Duplicate configuration to open a prefilled Build Project form, edit it, then select Review copy. Creating the copy leaves it disabled with no webhook and does not start a build. You need permission to read the source and create Build Projects, plus access to the selected dependencies.
The copy keeps Git, registry, execution-target and secret references. Review image repository names and tag templates before enabling it: unchanged output settings can publish over the source project's tags. Runs, artifacts, webhook credentials and access grants are not copied. The creation activity identifies the source project.
Source And Branches
The build source is an existing Citadel Git repository.
When a repository is selected, Citadel discovers branches and shows them in the branch field. If branch discovery fails, check repository credentials and sync status.
The context and Dockerfile paths are relative to the repository root:
Context: .
Dockerfile: DockerfileFor a monorepo:
Context: services/api
Dockerfile: services/api/DockerfilePaths must stay inside the repository. Absolute paths and path traversal are rejected.
Build Context Size
The build context is the directory sent to Docker. Keep it small and explicit.
For local builds, a large context slows the Docker API upload and can make logs appear delayed.
For Agent, Edge Agent, and remote Build Pool execution, Core creates a Git tar archive of the selected context with a 12 MiB output limit. This happens before transfer through the 16 MiB Agent envelope. An oversized archive fails before Docker starts, and the Dockerfile must be inside the selected context.
The archive step does not apply .dockerignore. Reduce the selected context or
remove unnecessary tracked files to reduce the transfer size. A .dockerignore
file alone does not fix an oversized remote archive.
Use .dockerignore to control the files Docker needs for a local directory build.
Common exclusions:
.git
node_modules
bin
obj
dist
coverage
*.logIn monorepos, prefer the smallest service directory as the context instead of using the repository root.
Build Pools
To run builds on dedicated builder infrastructure, configure a Build Pool and select it as the project's runner. The pool documentation covers inbound Agent and Edge Agent setup, connection status, and availability alerts.
Image Repository And Tags
The registry supplies the host. The image repository should not include a registry host, tag, or digest.
Examples:
team/api
internal/jobs/report-workerTag templates support:
{branch}: sanitized branch name{shortSha}: first 12 characters of the resolved commit{sha}: full resolved commit SHA
Common tag templates:
{branch}-{shortSha}
{branch}, {branch}-{shortSha}
latest, {shortSha}Citadel resolves the final image references when the run resolves the Git commit.
Build without publishing
Disable Push to registry to keep the output on the selected builder. Registry and image-repository settings are then cleared. The run records its local image references, but another Platform cannot pull those images automatically. Builder cleanup can remove them, especially on ephemeral infrastructure. Keep publishing enabled for the build-to-deployment workflow above.
Using Build Output
Build projects produce image references. Deployments and stacks consume those image references from their own forms.
Deployments
In a deployment, choose:
- Image Source:
Build - Build: the build project that produces the image
- Redeploy On Build: whether Citadel should redeploy this deployment automatically after a successful build
Redeploy On Build requires Automated Operations. Without it, Citadel still
records the new desired artifact and shows it as pending, but an operator must
select Deploy or Redeploy manually.
The deployment form shows the latest build artifact, the artifact currently desired by the deployment, and the artifact last applied successfully. You can save the deployment before the first successful build, but the deployment cannot be applied from that build image until a run succeeds.
When a build succeeds, Citadel updates each deployment that uses that build image source with the new desired image reference and digest, records a DeploymentUpdated activity event, streams the deployment update to connected clients, and writes the update to the build run log. If Redeploy On Build is enabled, Citadel redeploys the deployment after the build finishes.
Deployment uses the saved desired artifact. A newer build arriving during deployment does not change the image being deployed. Citadel pins the image digest when available and records it as applied only after the deployment starts successfully. A failed redeploy leaves the desired artifact pending.
Stacks
In a web editor stack or Git stack, use Build Images to map Compose services to build projects.
Each binding contains:
- Compose Service: exact Compose service name, such as
api - Build: the build project that produces that service image
- Redeploy On Build: whether Citadel should redeploy that service after a successful build
Redeploy On Build requires Automated Operations. Without it, Citadel records
the desired artifact but does not automatically apply the stack.
When the stack is applied, Citadel uses the desired artifact stored on each binding. A binding without a resolved artifact falls back to the latest successful build. For web editor stacks, Citadel replaces the service image in the generated Compose content. For Git stacks, Citadel writes a generated Compose override file with the resolved image references.
When a build succeeds, Citadel updates matching stack build image bindings with the new desired image reference and digest, records a StackUpdated activity event, streams the stack update to connected clients, and writes the affected service names to the build run log. If Redeploy On Build is enabled, Citadel reapplies only the mapped services. Applied provenance changes only for services that were included in a successful apply.
This service-scoped redeployment is supported only for Docker Standalone Stacks. For a Swarm Stack, leave Redeploy On Build disabled and deploy the complete Stack manually after the desired build image changes.
If a mapped build has no successful image yet, save is allowed, but stack apply fails until that build has a successful run.
Build Arguments
Build arguments are passed to Docker with --build-arg.
Example:
[
{
"name": "NODE_ENV",
"value": "production"
}
]Do not put passwords, tokens, or private keys in build arguments. Docker build args can leak into image history or metadata.
Store sensitive values as Citadel secrets and reference them from Build secrets instead of typing raw secret values in the build form.
Build Secrets
Build secrets map a BuildKit secret id to an existing Citadel secret. The BuildKit id must match the id used by the Dockerfile, and the value must come from a Citadel secret.
Build secrets are not raw name/value pairs. Configure them by selecting an existing Citadel secret from the build form. The text id you enter is only the Dockerfile mount id, for example npmrc, pip_index_url, or github_token.
The Dockerfile consumes the secret with BuildKit syntax:
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
npm ciThis example assumes the install step runs as root. Adjust the mount path and ownership for another build user. Keep the credential on the temporary mount; copying it into the image filesystem can persist it in an image layer. See Docker's secret mounts.
Citadel resolves stored secrets during execution and passes them to BuildKit. Keep values small and do not print them from the Dockerfile. Log redaction cannot prevent a Dockerfile from writing a credential into the output image.
Run A Build
Use Build from the build list or build detail page.
A run:
- claims the queued run
- syncs the Git repository branch
- resolves the exact commit SHA
- validates the context and Dockerfile paths
- resolves build args, resolves Citadel-backed build secret mappings, and resolves registry credentials
- builds the image on the selected builder
- pushes generated image tags when Push to registry is enabled
- updates deployment and stack build-image consumers when the build succeeds
- redeploys supported consumers that have
Redeploy On Buildenabled - stores run status, image references, digest when available, and logs
Step 9 requires Automated Operations. Without it, the desired artifacts in
step 8 remain pending until an operator applies the consumer manually.
Only one run per build project can be active at a time. If a run is already queued, preparing, or running, Citadel rejects another run for the same project.
Webhooks
Webhook-triggered builds require Automated Operations. Builds using an
external Build Pool additionally require Elastic Build Execution.
Enable webhooks on a build project when a Git provider should queue a build after a push.
Build webhook runs use the same configured repository, branch, context, Dockerfile, platform, registry, and tag templates as manual runs. Citadel validates the provider secret, branch, and repository identity, then queues the build with trigger Webhook.
Citadel uses changed paths from the webhook payload when the provider includes them. A build is queued only when a changed path is inside the configured build context or exactly matches the configured Dockerfile path.
If the payload does not include changed paths, Citadel syncs the repository branch, diffs the latest successful build commit against the new branch head, and applies the same path decision. This keeps monorepo builds from running when only unrelated services changed.
Only one run per build project can be active at a time. If a webhook arrives while a run is already queued, preparing, or running, Citadel rejects the delivery instead of starting another build.
Build Webhook Received activities show the delivery outcome:
- Success: the build was queued. This does not mean the build has succeeded.
- Information: a configuration skip, such as a disabled build project.
- Warning: a build could not be queued because the repository did not match, a required entitlement was unavailable, another run was active, or the configuration changed during dispatch. Other unexplained skips are warnings too.
- Failure: authentication, request validation, or dispatch failed.
The activity summary includes the outcome reason. Routine no-ops (no relevant changes, no new commit, or a filtered branch or event) do not create activity entries; the webhook response still contains the reason.
For the shared listener URL format and provider setup, see Webhooks.
Logs And History
Open the Runs tab on a build project to see run history.
Use the log action on a run to view persisted logs. Active runs stream logs live while the sheet is open. Logs are stored with the run until retention removes that run.
Run statuses:
Queued: waiting for the build worker.Preparing: worker claimed the run and is preparing source, secrets, and registry auth.Running: Docker build or push is active.Succeeded: build completed, including push when enabled.Failed: build, push, validation, or setup failed.TimedOut: the run exceeded its timeout.Cancelled: a user cancelled the run.Interrupted: Citadel stopped or lost the active execution session.
Cancellation
Use the cancel action on an active run.
Cancellation is best-effort:
- queued runs are marked cancelled before they start
- preparing or running runs receive a cancellation signal
- Citadel updates the build project back to idle after the run becomes terminal
If Citadel restarts during a run, the reconciler marks orphaned active runs as interrupted.
Resolving retained build capacity
A failed or interrupted build may keep its capacity reserved when Citadel cannot confirm that the build and registry pushes stopped. Automatic reconciliation releases that capacity when the original Agent confirms cleanup, without retrying the build.
For unresolved terminal runs, administrators see Cleanup pending → Resolve cleanup in the run log sheet:
- Check the original execution host and stop any remaining build or registry pushes for the displayed attempt.
- Once other builds on the host finish, restart the original Agent so delayed commands from the old attempt are fenced.
- Enter resolution notes and confirm that the work has stopped, then select Release capacity.
Citadel must reach the restarted Agent on the same Docker daemon. An offline, removed, or different-daemon target stays reserved until the original target is available. Restarting the Agent alone does not prove Docker work stopped; the administrator must verify that first. Resolution records the administrator, attempt, daemon, old/new Agent identities and notes in Activities and run logs. It preserves the original build result and completion time and does not retry the build.
Core-local builds also retain uncertain daemon occupancy. For those runs, restart Core after verifying that its old build/push processes stopped; recovery checks Core's new authority against the same local Docker daemon.
Retention
Each build project keeps its most recent terminal runs according to Run retention.
When a new terminal run is recorded, Citadel deletes older terminal runs beyond the retention count. Persisted logs for deleted runs are removed with the run.
Citadel also uses the existing cleanup job to remove old terminal build runs. By default, completed build runs older than 90 days are deleted during the normal cleanup cycle. Administrators can configure this with:
CITADEL_BUILDS_RUN_CLEANUP_ENABLED
CITADEL_BUILDS_RUN_RETENTION_DAYSRetention does not delete:
- pushed registry images
- Git commits
- local platform images
- activity records required by the audit model
Troubleshooting
If no platform appears in the build form:
- create a Docker platform
- confirm the platform is accessible from Citadel Core
- for agent and edge-agent platforms, confirm the agent is running a build-capable version
If branch discovery is empty:
- sync the Git repository
- confirm repository credentials are valid
- confirm the branch exists on the remote
If the build fails before Docker starts:
- check that Context exists in the repository
- check that Dockerfile exists
- confirm paths are relative to the repository root
- for remote builds, keep the committed context tar within the 12 MiB archive limit;
.dockerignoreis not applied by the archive step - for build-pool Edge Agent builds, confirm the pool's Edge Agent is connected and uses a build-capable Agent version
- confirm the selected registry has push credentials when required
- if build secrets are configured, confirm each BuildKit id maps to an existing Citadel secret selected in the build form
If Docker reports NotFound: secret not found while resolving the build graph, one of the Dockerfile RUN --mount=type=secret,id=... entries does not have a matching build secret mapping, or the selected Citadel secret is no longer available to the run.
If the push fails:
- confirm the registry host and image repository are correct
- confirm the registry token has push permission
- confirm the selected builder can reach the registry
If a deployment or stack does not update after a successful build:
- confirm the deployment uses Image Source
Build, notExternalorLocal - confirm the stack has a Build Images binding for the exact Compose service name
- confirm the selected build project is the one that just succeeded
- confirm
Redeploy On Buildis enabled if you expected an automatic redeploy - check the build run log for consumer update messages
A queued build keeps the build settings selected when it was queued. Editing the project does not change a build that is already waiting to run.
A successful build and a successful redeployment are separate outcomes. If the image builds successfully but deployment fails, the build remains successful; check the Deployment or Stack activity for the deployment error. For Stack bindings with Redeploy On Build, only the selected services are redeployed.
If secrets appear masked in logs, that is expected. Citadel redacts configured secret values before storing or streaming log output.