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.
- 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)"
- 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.
- Install it:
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:
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) anddetailinstead ofmsgandstatus_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
403and404are answered with500, 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
runAsUsermay 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.