Skip to content

Deployment

The chart in deployment/ installs the sidecar into the namespace of the Deployments to upgrade, on Kubernetes and on OpenShift. It needs no fixed user ID, so OpenShift's default security constraints apply.

  1. Create a Secret with a token of its own for this site:
kubectl -n production create secret generic pubgrade-sidecar \
  --from-literal=access-token="$(openssl rand -hex 32)"
  1. Write the values, e.g., my-values.yaml:
targetDeployments:
  - tesk-api
accessToken:
  existingSecret: pubgrade-sidecar
hostName: sidecar.example.org
# On OpenShift:
route:
  enabled: true
# Elsewhere, instead:
# ingress:
#   enabled: true
#   className: nginx
#   tlsSecretName: sidecar-tls

Without a Route or Ingress, the sidecar is only reachable in the cluster. A Route redirects HTTP to HTTPS. With an Ingress, that depends on the controller (ingress-nginx does it by default if TLS is set): check it, and set ingress.annotations if needed, as every call carries the token.

  1. Install it:
helm install pubgrade-sidecar deployment/ -n production -f my-values.yaml

The chart deploys the image of its appVersion unless image.tag is set (see Image). Until the first release, that version has no image yet: set image.tag to sha-<commit> or dev.

PUT /deployment updates all targetDeployments that use the notified image repository (see API). To limit it to some of them, set deploymentLabel to a label they have, key=value, or key for any value (a value without a key, e.g., =api, matches no deployment); PUT /deployment/{name} isn't limited by it. Outside the chart, the sidecar takes these settings from the environment variables TARGET_DEPLOYMENTS (comma-separated names; without it, all deployments in the namespace, which needs the right to list them) and DEPLOYMENT_LABEL.

The sidecar gets get and patch on the targetDeployments and nothing else. So with the chart, GET /deployment and DELETE /deployment/{name} are refused: they would need further rights, which you can add with a Role of your own. Kubernetes can't limit patch to one field, so these rights cover the whole of each listed Deployment, although the sidecar's API only changes the image tag. List only what the sidecar has to update, and add an admission policy in your cluster if you need a stricter limit. All values are described in deployment/values.yaml.

To change the token, update the Secret and restart the sidecar, which reads the token at start:

kubectl -n production rollout restart deployment/pubgrade-sidecar

Registries

Before a rollout, the sidecar checks that the image is in its registry, with the OCI distribution API: Docker Hub for repositories without a registry host, otherwise the registry named in the repository (e.g., a Harbor project, harbor.example.org/project/tesk-api). It asks anonymously over HTTPS, which suffices for public images. For private images, create a Secret with credentials that may pull them (e.g., a Harbor robot account), and name it in registryCredentials.existingSecret:

kubectl -n production create secret docker-registry registry-credentials \
  --docker-server=harbor.example.org \
  --docker-username='robot$sidecar' --docker-password=<secret>

The chart mounts its key .dockerconfigjson (set another one with registryCredentials.key, e.g., for a Docker config.json) and passes the path in REGISTRY_AUTH_FILE. Only its auths entries are read, with auth or username and password (not credential helpers or identity tokens). If the registry names a token service, as Docker Hub and Harbor do, the credentials go to that service, as with Docker's own client, but only over HTTPS (or to an insecure registry itself); token services over HTTP aren't used otherwise, and neither are challenges after a redirect to another host or from HTTPS to HTTP. The sidecar only uses the credentials to check images; the cluster pulls images with the Deployments' own imagePullSecrets. Registries that serve only HTTP, e.g., one in the cluster, go in insecureRegistries (host or host:port; environment variable INSECURE_REGISTRIES, comma-separated); credentials for them are sent unencrypted.

Changed image tags (November 2026)

Until the release workflow was added (November 2026), every push to dev moved the image tag latest (and a dated tag, YYYYMMDD), and the chart deployed latest by default. Now pushes to dev publish dev and sha-<commit>, latest moves only with releases, to the highest stable release, and dated tags are no longer published (existing ones stay as they are). The chart deploys the image of its appVersion unless image.tag is set. So latest stays at the last image published from dev before the change until the first release. If you pull latest (or deployed the chart with its default tag) to follow development, switch to sha-<commit> (or dev), or to a release once there is one; until the first release, set image.tag when you install the chart (see above).

Upgrading to version 3.1.0 of the API

Version 3.1.0 of the API adds PUT /deployment, for all deployments that use an image repository (see API), and the chart value deploymentLabel. Existing calls stay, with one change: PUT /deployment/{deployment-name} updates every container of the deployment whose image repository equals image_name. Before, it updated only the first container, and answered 409 if that one used another repository; now a 409 means that no container uses it.

Images are no longer limited to Docker Hub: the sidecar checks for them in the registry named in the repository, with optional credentials and the chart values registryCredentials and insecureRegistries (see Registries). Repositories on other registries, which got a 409 before, are now rolled out.

Upgrading to version 3.0.0 of the API

Version 3.0.0 of the API (November 2026) runs on FOCA 0.21 and Python 3.13 instead of FOCA 0.7 and Python 3.9. Calls and their successful responses stay the same, and so do the chart and its values. What changes:

  • Errors are problem details (application/problem+json, see API): title, status (an integer) and detail instead of msg and status_code (a string). Clients that read the error body need to change; Pubgrade only reads the status code.
  • Errors of the Kubernetes API other than 403 and 404 are answered with 500, as documented, instead of passing on their status (e.g., 503).
  • Request bodies larger than 64 KiB are rejected with 413.
  • The image serves the app with Gunicorn instead of Flask's development server, still on port 8080, with one worker process (keep it at one: the sidecar keeps its deliveries in memory). Gunicorn's settings can be changed via GUNICORN_CMD_ARGS.
  • The image runs as user 1000 (group 0), the user of the FOCA image, instead of 1005, and its app files are read-only. The chart sets no fixed user, so it needs no change; deployments of their own that set runAsUser may set any user ID.

Upgrading from chart version 0.1

Uninstall the old release first (helm uninstall), then install as above: Kubernetes doesn't allow changing the role of an existing RoleBinding, so helm upgrade fails. The values changed:

Old                  New
appname              appName
imagename            image.repository, image.tag
serviceaccountname   appName
containerport        service.port
secret               accessToken.existingSecret, a Secret that you create
clusterType          route.enabled or ingress.enabled
nodeport             none
none                 targetDeployments, required

Image

The image is published as elixircloud/pubgrade-sidecar, with these tags:

Tag              Image                                     Moves
X.Y.Z            release X.Y.Z (also X.Y.ZrcN etc.)        no
latest           highest stable release                    with each release
dev              newest commit on branch dev               with each push to dev
sha-<commit>     that commit on branch dev                 no

Releases are signed (Sigstore) and come with build provenance attestations; each GitHub release also has the chart of that version attached. The chart deploys the image of its appVersion, i.e., of its release, unless you set image.tag. To try unreleased changes, set image.tag to sha-<commit> (the only tag of a dev image that stays on one commit) or dev.

Images from dev are published by CI on every push to dev, after all checks have passed, in the environment dev-images, which allows only the branch dev. Releases are published from version tags, in the environment release (see Versioning and releases). Both need the Docker Hub secrets DOCKERHUB_LOGIN and DOCKERHUB_TOKEN (organization secrets).

A fork publishes to its own Docker Hub organization once it sets these secrets and the Actions variable DOCKERHUB_ORG (for the repository or organization, not the environment); otherwise it skips publishing. To deploy a fork's image, set image.repository to <organization>/pubgrade-sidecar.