Pull Stackweaver images from GHCR
Create a namespace-scoped pull Secret for the private images your installation needs. Public container images can be pulled anonymously; access to a public source repository does not establish access to its container package.
1. Confirm package access
The deployment currently needs private API, frontend, orchestrator, OpenTofu runner and
Zitadel-init images under ghcr.io/vhco-pro. The Ansible runner and secrets-init images are
public. Check the exact package and tag you will deploy if a pull is denied.
Request package access from [email protected], including the GitHub username that
will pull images. Accept any organization invitation you receive. Organization membership
alone is not a guarantee: the account must have read access to the relevant packages.
For shared infrastructure, use an account whose lifecycle is managed by the operating team.
In GitHub, open Settings > Developer settings > Personal access tokens > Tokens (classic).
Create a token with read:packages, an expiry that fits your rotation policy, and SSO
authorization if your organization requires it. Save it in a secret store. GitHub Packages
uses classic PATs for this path; see its
container-registry authentication guidance.
2. Create the Kubernetes Secret
Use kubectl against the intended cluster. The namespace must exist; create it before the
Secret if this is a first installation. Use the same namespace as the Helm release.
Set GITHUB_USERNAME, GHCR_TOKEN and NAMESPACE through your shell or secret tooling, then:
kubectl create secret docker-registry ghcr-pull-secret \
--docker-server=ghcr.io \
--docker-username="$GITHUB_USERNAME" \
--docker-password="$GHCR_TOKEN" \
--namespace="$NAMESPACE"
kubectl get secret ghcr-pull-secret --namespace "$NAMESPACE"
The expected type is kubernetes.io/dockerconfigjson. Do not commit its unencrypted YAML or
print its contents into review logs. Supplying a real token as a CLI argument can expose it
to local process inspection; use your platform's secret-management path where required.
3. Wire it into the chart
Save as pull-secret-values.yaml:
global:
imagePullSecrets:
- name: ghcr-pull-secret
For a new release, merge this with the Helm installation values. For an existing
release, set CHART_VERSION to the installed chart version and preserve the other values:
helm upgrade stackweaver oci://ghcr.io/vhco-pro/charts/stackweaver \
--namespace "$NAMESPACE" --version "$CHART_VERSION" \
--reuse-values --values pull-secret-values.yaml
All chart-managed workload pods use this reference, including hook Jobs. The Secret must exist before those pods need to pull images. A registry login on your laptop does not supply credentials to Kubernetes nodes.
4. Verify a pull
kubectl get pods --namespace "$NAMESPACE"
kubectl describe pod POD_NAME --namespace "$NAMESPACE"
Replace POD_NAME with an affected pod. Read its image-pull events: unauthorized suggests
package/token access; not found may mean an incorrect repository or tag. Check the pod's
imagePullSecrets reference and that the Secret exists in the same namespace. Successful
Secret creation alone does not prove image access.
Rotate without deleting the Secret
Generate a replacement token with the same required access, set GHCR_TOKEN to that value
and update the existing Secret:
kubectl create secret docker-registry ghcr-pull-secret \
--docker-server=ghcr.io \
--docker-username="$GITHUB_USERNAME" \
--docker-password="$GHCR_TOKEN" \
--namespace="$NAMESPACE" \
--dry-run=client --output=yaml | kubectl apply -f -
Verify the replacement can pull the configured images before revoking the old token. Existing running containers do not prove a future image pull will succeed. Secret names and chart values can remain unchanged.
Docker Compose
The same account and token can authenticate the local Docker client:
printf '%s' "$GHCR_TOKEN" | docker login ghcr.io --username "$GITHUB_USERNAME" --password-stdin
Then follow the Compose installation. This does not create a Kubernetes Secret; the two clients store credentials separately.