Deploying Wolpi on Kubernetes
If you run your services in a Kubernetes cluster, this guide helps you set it up. It focuses on
a very basic setup without extensions. It also does not describe how to set up an Ingress for
the service, since that is highly dependent on the type of ingress controller you use.
Prerequisites
- You can deploy resources in a Kubernetes namespace.
- You can access the GitHub Container Repository from the cluster, or you have deployed a custom container to a private registry that you can access from the cluster.
Create the runtime configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: wolpi-config
data:
wolpi.yml: |-
http:
host: "0.0.0.0"
port: 8080
extension-pool:
min-idle: 16
max-idle: 16
max-total: 16
Customize the configuration to fit your environment. This example pins 16
extension contexts per extension to keep throughput predictable.
Note
extension-pool sizing is workload-dependent. Tune these values for your
traffic and available memory; see
Extension Pool Configuration.
Deploy Wolpi
apiVersion: apps/v1
kind: Deployment
metadata:
name: wolpi
spec:
replicas: 2
selector:
matchLabels:
app: wolpi
template:
metadata:
labels:
app: wolpi
spec:
containers:
- name: wolpi
image: ghcr.io/dbmdz/wolpi:latest
imagePullPolicy: Always
ports:
- containerPort: 8080
name: http
volumeMounts:
- name: wolpi-config
mountPath: /app/wolpi.yml
subPath: wolpi.yml
env:
- name: JAVA_TOOL_OPTIONS
value: -XX:MaxRAMPercentage=50.0 -XX:InitialRAMPercentage=12.5
- name: VIPS_CONCURRENCY
value: "1"
resources:
requests:
memory: 1Gi
cpu: "4"
limits:
memory: 4Gi
cpu: "8"
livenessProbe:
httpGet:
path: /monitoring/health/liveness
port: http
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /monitoring/health/readiness
port: http
initialDelaySeconds: 10
periodSeconds: 5
volumes:
- name: wolpi-config
configMap:
name: wolpi-config
The example starts two replicas. Wolpi itself does not keep request or session state, so serving
traffic scales horizontally. If you install extensions at runtime or otherwise write files under
/app/data, make that data available to every replica (e.g. through a shared PVC) or
bake it into a custom image.
To serve your own images, mount a PVC or NFS volume to /app/images inside the container so images
can be resolved by relative path. If your identifiers do not map directly to file paths, add an
extension-based resolver such as the Pattern Resolver.
Note
The example uses ghcr.io/dbmdz/wolpi:latest for simplicity. For production, pin to a
specific version or digest so rollouts are reproducible.
Note
The JAVA_TOOL_OPTIONS, VIPS_CONCURRENCY, and CPU/memory values shown here are starting
points, not universal defaults. If you need to tune pod resources or runtime settings, see
the Memory Usage and
Concurrency sections in
the optimization guide. If you use Vertical Pod Autoscaler, start with a conservative
extension-pool.min-idle value and let the pool grow under load instead of pinning many warm
contexts in memory all the time.
Expose Wolpi inside the cluster
apiVersion: v1
kind: Service
metadata:
name: wolpi
labels:
app: wolpi
spec:
selector:
app: wolpi
ports:
- name: http
protocol: TCP
port: 80
targetPort: http
Note
If you expose Wolpi through an Ingress or reverse proxy on a public host or path prefix,
make sure it forwards the Host and X-Forwarded-* headers correctly. If that is not
possible, set http.base-uri in wolpi.yml to the public base URL so generated id values
and canonical links use the public URL. See also
Expose Wolpi Behind a Reverse Proxy and the
HTTP integration reference.
Add metrics scraping (optional)
If you run the Prometheus Operator, add a ServiceMonitor so Prometheus can scrape
Wolpi metrics.
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: wolpi
labels:
release: <prometheus-release>
spec:
namespaceSelector:
matchNames:
- <namespace>
selector:
matchLabels:
app: wolpi
endpoints:
- port: http
path: /monitoring/prometheus
interval: 30s
Set the release label to whatever your Prometheus installation uses to select ServiceMonitor
resources. Some setups use release: prometheus, while others use a different label or no label
selector at all.
Apply resources
$ kubectl -n <namespace> apply -f wolpi-configmap.yaml
$ kubectl -n <namespace> apply -f wolpi-deployment.yaml
$ kubectl -n <namespace> apply -f wolpi-service.yaml
$ kubectl -n <namespace> apply -f wolpi-servicemonitor.yaml
Skip the last command if you do not use Prometheus Operator.
Verify deployment
$ kubectl -n <namespace> rollout status deploy/wolpi
$ kubectl -n <namespace> get pods -l app=wolpi
$ kubectl -n <namespace> port-forward svc/wolpi 8080:80
If you created the ServiceMonitor, you can also verify that it exists:
In a second terminal, query Wolpi's built-in validation image. This lets you verify the deployment without having your own images mounted:
$ curl -s http://localhost:8080/v3/67352ccc-d1b0-11e1-89ae-279075081939/info.json | jq '.sizes'
[
{
"type": "Size",
"width": 1000,
"height": 1000
}
]
If you've set up Prometheus, run a query to verify Wolpi metrics are being ingested:
Next steps
- If you expose the service publicly through an ingress or gateway, see Expose Wolpi Behind a Reverse Proxy.
- For redirects, caching, and response behavior at the IIIF endpoints, see HTTP Integration.
- For runtime tuning, see Optimizing Wolpi Configuration.
- For extension configuration examples, see Using Extensions.