-
Notifications
You must be signed in to change notification settings - Fork 30
Kubernetes
Helm chart: deploy/helm/niks3.
Image: ghcr.io/mic92/niks3 (amd64/arm64, contains niks3-server and niks3).
niks3 is stateless. Multiple replicas are fine.
helm install niks3 oci://ghcr.io/mic92/charts/niks3 -n niks3 --create-namespace -f values.yamldatabase:
existingSecret: niks3-pg-app # key `uri`
s3:
endpoint: s3.eu-central-1.amazonaws.com
bucket: my-nix-cache
useIAM: true
serviceAccount:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/niks3
auth:
existingSecret: niks3-token # key `token`, >= 36 chars
signing:
existingSecret: niks3-signing
keys: [cache.example.com-1]
cacheURL: https://cache.example.com
ingress:
enabled: true
className: nginx
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "0"
hosts:
- host: niks3.example.com
paths: [{ path: /, pathType: Prefix }]helm show values oci://ghcr.io/mic92/charts/niks3 lists everything else.
On OpenShift set podSecurityContext.runAsUser: null and runAsGroup: null so the SCC can assign a UID.
One connection string, mounted from a Secret as NIKS3_DB_FILE. Migrations
run on startup.
- CloudNativePG:
database.existingSecret: <cluster>-app(keyuriis the default). - RDS / Cloud SQL: put the DSN in a Secret. For IAM auth run the vendor proxy
as sidecar and point at
localhost. -
database.urlinline for quick tests. - With neither set, the server uses
PGHOST/PGUSER/... fromextraEnv.
s3.useIAM: true for IRSA / Pod Identity / GKE Workload Identity, otherwise a
Secret with access-key / secret-key.
Clients upload straight to S3 via presigned URLs, so the S3 host must be
reachable from wherever niks3 push runs. For in-cluster MinIO/Garage, let the
server use the Service and presign against the public route:
endpoint: minio.minio.svc:9000, useSSL: false, bucketLookup: path,
publicURL: https://s3.example.com.
-
/healthzliveness,/readyzreadiness (503 while Postgres is down) -
/metricsfor Prometheus.metrics.serviceMonitor.enabledadds a ServiceMonitor - GC is a CronJob (
gc.schedule,gc.olderThan,gc.failedUploadsOlderThan)
Service account tokens are OIDC JWTs, so in-cluster builders can push without the shared API token:
auth:
workloadIdentity:
enabled: true
allowedServiceAccounts: ["ci:builder", "ci:nix-*"]
scopes: [write] # add admin for GC/pin deletion. Per-subject rules: see OIDC, via auth.oidcProviders
# issuer must equal: kubectl get --raw /.well-known/openid-configuration | jq -r .issuerBuilder pod:
spec:
serviceAccountName: builder
containers:
- name: build
env:
- { name: NIKS3_SERVER_URL, value: http://niks3.niks3.svc }
- { name: NIKS3_AUTH_TOKEN_FILE, value: /var/run/secrets/niks3/token }
volumeMounts:
- { name: niks3-token, mountPath: /var/run/secrets/niks3, readOnly: true }
volumes:
- name: niks3-token
projected:
sources:
- serviceAccountToken: { audience: niks3, path: token }niks3 push ./result then authenticates as system:serviceaccount:ci:builder.
Outside a pod: kubectl -n ci create token builder --audience niks3.
Under the hood this is an OIDC provider with bound_subject plus
ca_file / bearer_token_file pointing at the pod's service account mount,
because the API server serves JWKS behind the cluster CA to authenticated
clients only. auth.oidcProviders adds further providers (GitHub, GitLab) to
the same config.