Use this guide to run Artifact Server in an existing Kubernetes cluster. The Helm chart installs only the application workload.
You must supply PostgreSQL, S3-compatible object storage, DNS, TLS certificates, routing, secrets, and one browser identity provider.
The chart source is in packaging/helm/artifact-server. A public chart package is not available yet.
Requirements
| Requirement | Supported configuration |
|---|---|
| Kubernetes | Versions 1.34, 1.35, or 1.36 |
| Helm | Version 4.2.x |
| Application image | The released OCI image, pinned by SHA-256 digest |
| Database | An existing PostgreSQL database |
| Object storage | An existing S3-compatible bucket |
| Routing | An Ingress, Gateway, or provider edge that routes two hosts |
| TLS | One application certificate and one wildcard-content certificate |
| Browser authentication | WorkOS or one OpenID Connect (OIDC) provider |
| Secrets | One existing Kubernetes Secret in the release namespace |
| Recovery | Provider procedures for PostgreSQL and object storage |
Run the commands in this guide from the repository root. Use a released image digest for a team installation.
Prepare the external services
Create one PostgreSQL database for the installation. Create one S3-compatible bucket for the same installation.
Use encrypted provider connections. Give Artifact Server access only to its database and bucket.
When possible, use a dedicated bucket. A dedicated bucket makes complete backup and restore operations easier to define.
Create these two public DNS names:
artifacts.example.comfor the trusted application.*.content.example.netfor isolated artifact versions.
The two names must use separate registrable domains. The routing layer sends both names to the same private ClusterIP Service.
Create the runtime Secret
Keep all credential values outside the repository and Helm values. The chart mounts Secret keys as read-only files.
Create the bootstrap service key in as_key_key_<id>_<secret> form:
install -d -m 0700 /secure/artifact-server
printf 'as_key_key_%s_%s\n' "$(openssl rand -hex 16)" "$(openssl rand -hex 32)" \
> /secure/artifact-server/api-token
chmod 0400 /secure/artifact-server/api-tokenPlace the PostgreSQL connection URL in /secure/artifact-server/database-url. Protect this file with mode 0400.
If you use static S3 credentials, place them in two files. When you use workload identity, skip these files.
/secure/artifact-server/s3-access-key-id
/secure/artifact-server/s3-secret-access-keyCreate the namespace and Secret for static S3 credentials:
kubectl create namespace artifact-server
kubectl --namespace artifact-server create secret generic artifact-server-runtime \
--from-file=api-token=/secure/artifact-server/api-token \
--from-file=database-url=/secure/artifact-server/database-url \
--from-file=s3-access-key-id=/secure/artifact-server/s3-access-key-id \
--from-file=s3-secret-access-key=/secure/artifact-server/s3-secret-access-keyIf you use workload identity, create the Secret without the two S3 key files:
kubectl --namespace artifact-server create secret generic artifact-server-runtime \
--from-file=api-token=/secure/artifact-server/api-token \
--from-file=database-url=/secure/artifact-server/database-urlThe first startup stores the bootstrap service key as a managed API key. Later startups require the exact same key.
Protect the Secret with your cluster recovery system. A restored database will reject a different bootstrap key.
Configure browser authentication
Each team installation requires exactly one browser identity provider. The chart rejects an incomplete provider configuration.
Generic OIDC
Register this redirect URI with the provider:
https://artifacts.example.com/auth/callbackUse the exact issuer URL and client ID in the Helm values. The default scope string is openid email profile.
If the client requires a secret, add the secret file to artifact-server-runtime. Then name its key with secret.keys.oidcClientSecret.
WorkOS
Set identity.workosClientId and identity.workosIssuer in the Helm values. The issuer is the exact HTTPS AuthKit origin.
Add the WorkOS API key to artifact-server-runtime. Then name its key with secret.keys.workosApiKey.
Do not configure WorkOS and OIDC together.
Create the Helm values
Create artifact-server-values.yaml. Replace every example value before installation.
replicaCount: 2
image:
repository: ghcr.io/plannotator/artifact-server
digest: sha256:REPLACE_WITH_THE_RELEASE_DIGEST
configuration:
installationId: team-example
bootstrapAdministratorEmail: admin@example.com
applicationOrigin: https://artifacts.example.com
contentDomain: content.example.net
postgresConnectionBudget: 22
postgresPoolSize: 10
s3:
bucket: artifact-server-team-example
region: us-east-1
identity:
oidcClientId: artifact-server
oidcIssuer: https://idp.example.com/realms/main
secret:
name: artifact-server-runtime
rolloutChecksum: secret-version-1
keys:
s3AccessKeyId: s3-access-key-id
s3SecretAccessKey: s3-secret-access-keyThe chart rejects a mutable image tag by default. image.allowMutableTag exists only for local chart development.
The example connection budget covers two server pools, one migration connection, and one cleanup connection.
Use this formula for another replica count:
(replicaCount × postgresPoolSize) + migration connection + cleanup connection ≤ postgresConnectionBudgetThe chart rejects values that exceed the connection budget.
Configure workload identity
When the workload uses its cloud identity, leave both S3 key names empty.
Add the provider annotations to the service account:
secret:
keys:
s3AccessKeyId: ""
s3SecretAccessKey: ""
serviceAccount:
annotations:
example.com/workload-identity: replace-with-provider-value
automountServiceAccountToken: falseReplace the example annotation with the annotation for your identity system.
If the identity system requires the projected Kubernetes token, set automountServiceAccountToken to true.
Otherwise, keep automountServiceAccountToken set to false.
Configure routing and TLS
The chart creates a private ClusterIP Service. Ingress is disabled by default.
If your controller uses networking.k8s.io/v1, enable the included Ingress:
ingress:
enabled: true
className: nginx
applicationTlsSecretName: artifact-server-application-tls
contentTlsSecretName: artifact-server-content-tlsThe application TLS Secret must cover artifacts.example.com. The content TLS Secret must cover *.content.example.net.
The included Ingress requires an HTTPS application origin without a path or explicit port.
If you use Gateway API or a provider edge, leave ingress.enabled set to false. Route both hosts to the chart Service.
Install Artifact Server
Install the chart with Helm:
helm upgrade --install artifact-server ./packaging/helm/artifact-server \
--namespace artifact-server \
--values artifact-server-values.yaml \
--rollback-on-failure \
--waitHelm runs artifactserver migrate apply before each installation and upgrade. A failed migration blocks the Deployment change.
Successful migration Jobs are removed. Failed migration Jobs remain for inspection until the next Helm attempt.
Inspect a failed migration:
kubectl --namespace artifact-server logs job/artifact-server-migrateCorrect the provider or Secret failure. Then run the installation command again.
Verify the installation
Verify the rollout and the internal readiness endpoint:
kubectl --namespace artifact-server rollout status deployment/artifact-server
helm test artifact-server --namespace artifact-server --logs
kubectl --namespace artifact-server get pods,service,cronjobThe chart uses /health for startup and liveness probes. It uses /ready for readiness and the Helm test.
Verify both public and account-required delivery through the final HTTPS hosts. Then verify browser login and the /mcp endpoint.
Use the publishing guide and the MCP guide to verify this product behavior.
Staging cleanup
The chart creates a cleanup CronJob by default. It runs one bounded cleanup pass every 15 minutes.
Serving Pods use the external cleanup schedule. They do not run a second cleanup loop.
Change the schedule or limits under cleanup in the Helm values.
If another operator runs this command, you can disable the CronJob:
artifactserver maintenance cleanup-staging --once --mode external-storageBack up and restore
The chart does not create or restore database and object backups. Use the recovery procedures for your PostgreSQL and object providers.
Treat the database, the complete installation object set, and the runtime Secret as one recovery set.
Before each upgrade, complete these actions:
- Stop write traffic at the trusted application edge.
- Suspend the cleanup
CronJob. - Scale the Artifact Server Deployment to zero replicas.
- Create a transaction-consistent PostgreSQL backup.
- Copy the complete installation object set.
- Record the installation ID, image digest, provider backup IDs, and backup time.
- Scale the Deployment to its configured replica count.
- Resume the cleanup
CronJob. - Restore traffic at the trusted application edge.
Restore both providers into empty targets. Use the same installation ID and the exact runtime Secret from the recovery set.
After restoration, install the same image digest. Then verify readiness, stable IDs, private access, and exact artifact bytes.
Upgrade and rollback
Create and verify one recovery set before each upgrade. Then replace image.digest with the new release digest.
Run the installation command again. The migration Job completes before the rolling Deployment change.
The default Deployment uses two replicas, zero unavailable Pods, and one surge Pod during a rolling update.
Verify the new release before you remove the recovery set.
Helm rollback does not reverse a database migration. Verify that the old image supports the current schema before rollback.
List the release revisions:
helm history artifact-server --namespace artifact-serverRoll back to one compatible revision:
helm rollback artifact-server <revision> --namespace artifact-server --wait
helm test artifact-server --namespace artifact-server --logsUninstall
Remove the Artifact Server workloads:
helm uninstall artifact-server --namespace artifact-server --waitThis command does not remove the external Secret, PostgreSQL database, bucket, DNS records, or TLS certificates.
Verify a complete recovery set before you remove any durable provider resource.
Current limits
- The chart does not create a cluster, database, bucket, DNS zone, certificate, Ingress controller, or secret manager.
- The chart does not include a Horizontal Pod Autoscaler.
- The chart leaves
NetworkPolicydisabled until the operator supplies exact rules for the cluster network. - The kind gate uses
ClusterIPand port forwarding. It does not verify an Ingress controller or wildcard TLS. - The kind gate uses placeholder OIDC configuration. It does not complete browser authentication.
- The kind gate does not verify the scheduled execution of the cleanup
CronJob. - The kind gate preserves external providers through uninstall and reinstall. It does not perform a coordinated provider restore.
- The bounded kind read sample is a regression baseline. It is not a production capacity claim.
Read the chart README, values.yaml, and the runtime qualification test for implementation details.