Run packageR on Kubernetes
The packageR container starts the application directly. Prepare its runtime
database before the main container starts. The preferred packageR deployment
model keeps runtime state ephemeral and treats shares like other
configuration, with an init container creating them during the bootstrap
phase. An init container and an emptyDir volume make this setup explicit:
- The init container creates a new database, users, and public shares.
- The main container reads the prepared database and starts packageR.
- Kubernetes discards the database when it replaces the Pod. The next init container creates it again from the deployment configuration.
Keep passwords and object-storage credentials in Kubernetes Secrets. Prefer a workload identity when the object-storage service supports it. Pin the image to a release tag or digest for a production deployment.
Example
Create a Secret named package-r-secrets with these keys before you apply the
example:
admin-passwordshare-passwordaws-access-key-idaws-secret-access-key
apiVersion: apps/v1
kind: Deployment
metadata:
name: package-r
spec:
replicas: 1
selector:
matchLabels:
app: package-r
template:
metadata:
labels:
app: package-r
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 100
fsGroup: 100
initContainers:
- name: prepare-database
image: ghcr.io/versioneer-tech/package-r:latest
command: ["/bin/sh", "-ec"]
args:
- |
database=/state/package-r.db
work_database=/state/package-r.db.new
rm -f "$work_database"
export PACKAGE_R_DATABASE="$work_database"
/package-r config init \
--address=0.0.0.0 \
--root=my-bucket \
--stac-browser-url=https://stac-browser.example/external/
/package-r users add admin "$ADMIN_PASSWORD" \
--perm.create=true \
--perm.delete=true \
--perm.modify=true \
--perm.rename=true
/package-r shares add admin public / \
--password="$SHARE_PASSWORD" \
--catalog-name=openaerialmap-assets.parquet \
--asset-mappings='[{"from":"s3://data/","to":"."}]'
mv "$work_database" "$database"
env:
- name: ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: package-r-secrets
key: admin-password
- name: SHARE_PASSWORD
valueFrom:
secretKeyRef:
name: package-r-secrets
key: share-password
volumeMounts:
- name: state
mountPath: /state
containers:
- name: package-r
image: ghcr.io/versioneer-tech/package-r:latest
env:
- name: PACKAGE_R_DATABASE
value: /state/package-r.db
- name: PACKAGE_R_ADDRESS
value: 0.0.0.0
- name: PACKAGE_R_PORT
value: "8888"
- name: PACKAGE_R_ROOT
value: my-bucket
- name: PACKAGE_R_CORS_ALLOWED_ORIGINS
value: https://viewer.example.org
- name: XDG_CACHE_HOME
value: /cache
- name: AWS_REGION
value: us-east-1
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: package-r-secrets
key: aws-access-key-id
- name: AWS_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: package-r-secrets
key: aws-secret-access-key
ports:
- name: http
containerPort: 8888
readinessProbe:
httpGet:
path: /health
port: http
livenessProbe:
httpGet:
path: /health
port: http
volumeMounts:
- name: state
mountPath: /state
- name: cache
mountPath: /cache
volumes:
- name: state
emptyDir: {}
- name: cache
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: package-r
spec:
selector:
app: package-r
ports:
- name: http
port: 8888
targetPort: http
The main container can update runtime state in the database. These changes last for the life of the Pod only. A replacement Pod gets a new database from its init container and existing login sessions end. Run one replica because each Pod has its own database and session-signing key.
An empty --password value creates an unprotected share. An empty or omitted
--catalog-name value creates a share without a catalog endpoint. Use a
relative catalog path inside the shared prefix. Use --asset-mappings only
when catalog asset URLs do not match paths in that share.
Change the bucket, paths, authentication, permissions, and share definitions in the init-container command. Do not add bootstrap logic to the main container. See Configuration for the available settings.
An Ingress normally forwards packageR CORS response headers unchanged. Route
OPTIONS requests to packageR, and configure CORS in only one layer to avoid
duplicate or conflicting headers. CORS for presigned object-storage URLs must
still be configured on the object-storage service.