Skip to content

CKAN

Deploy CKAN on Kubernetes — the world’s leading open-source data management system for publishing, sharing, and discovering datasets. Powers government open-data portals and data hubs worldwide.

ckan.siteUrl is required — incorrect value breaks all dataset links and API responses

CKAN uses ckan.siteUrl to generate absolute URLs for datasets, resources, and API responses. Setting it to the wrong value (such as the default http://localhost:5000) causes all links to be incorrect and OAuth callbacks to fail. Always set it to the full public URL before deployment.

DataStore requires an externally prepared PostgreSQL database

The optional DataStore extension requires a separate datastore database with a dedicated read-only role. The bundled PostgreSQL topology supports the CKAN catalog only. Create the DataStore database and roles externally with the upstream permission script before enabling datastore and datapusher.

Key Features

  • Configurable uWSGI application — the real listener and probes follow ckan.containerPort
  • Optional DataPusher — CSV/Excel loading when an external DataStore is prepared
  • CKAN-specific Solr — ckan/ckan-solr StatefulSet with bundled search schema
  • PostgreSQL and Redis contracts — bundled or structured external connections with Secret-backed passwords
  • Three secrets — sysadmin password, Beaker session secret, JWT secret
  • pg_dump backup — daily PostgreSQL S3 backup CronJob
  • Secure defaults — dedicated ServiceAccount, no API token mount, hardened contexts, and NetworkPolicy

Installation

HTTPS repository:

helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install ckan helmforge/ckan -f values.yaml

OCI registry:

helm install ckan oci://ghcr.io/helmforgedev/helm/ckan -f values.yaml

Deployment Examples

# values.yaml — CKAN catalog with bundled PostgreSQL, Redis, and Solr
ckan:
  containerPort: 5000
  siteUrl: 'https://data.example.com' # required; wrong value breaks all links
  siteTitle: 'My Open Data Portal'
  sysadminName: admin
  sysadminEmail: [email protected]
  existingSecret: ckan-secrets
  existingSecretPasswordKey: sysadmin-password
  existingSecretSessionKey: session-secret
  existingSecretJwtKey: jwt-secret

postgresql:
  enabled: true
  auth:
    database: ckan
    username: ckan
    password: 'strong-db-password'

redis:
  enabled: true
  auth:
    password: 'strong-redis-password'

solr:
  enabled: true
  persistence:
    size: 10Gi

datapusher:
  enabled: false # requires an external, pre-provisioned DataStore

persistence:
  enabled: true
  size: 50Gi # uploaded datasets and resources

ingress:
  enabled: true
  ingressClassName: traefik
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  hosts:
    - host: data.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: ckan-tls
      hosts:
        - data.example.com
# values.yaml — CKAN with external PostgreSQL (both ckan + datastore DBs) and Redis
# Both databases must be created in PostgreSQL before deploying:
#   CREATE DATABASE ckan;
#   CREATE DATABASE datastore;
#   CREATE USER ckan WITH PASSWORD '...';
#   CREATE USER datastore_ro WITH PASSWORD '...';
#   GRANT ALL ON DATABASE ckan TO ckan;
#   GRANT CONNECT ON DATABASE datastore TO ckan;
#   GRANT CONNECT ON DATABASE datastore TO datastore_ro;

ckan:
  siteUrl: 'https://data.example.com'
  existingSecret: ckan-secrets
  plugins: 'envvars image_view text_view datatables_view datastore datapusher'
  extraEnv:
    - name: PGSSLROOTCERT
      value: /etc/ckan/postgresql-ca/ca.crt

postgresql:
  enabled: false

redis:
  enabled: false

database:
  mode: external
  external:
    host: postgres.database.svc.cluster.local
    port: 5432
    ckanDatabase: ckan
    datastoreDatabase: datastore
    username: ckan
    datastoreReadUsername: datastore_ro
    existingSecret: ckan-db-credentials
    existingSecretPasswordKey: password
    existingSecretDatastoreReadPasswordKey: datastore-read-password
    sslMode: verify-full

redisConfig:
  mode: external
  external:
    host: redis.cache.svc.cluster.local
    port: 6379
    database: 0
    tls: true
    sslCertReqs: required
    existingSecret: ckan-redis-credentials
    existingSecretPasswordKey: redis-password

datapusher:
  enabled: true

solr:
  enabled: true # always use the bundled CKAN-specific Solr image

extraVolumes:
  - name: postgresql-ca
    secret:
      secretName: postgresql-ca
extraVolumeMounts:
  - name: postgresql-ca
    mountPath: /etc/ckan/postgresql-ca
    readOnly: true

ingress:
  enabled: true
  ingressClassName: traefik
  hosts:
    - host: data.example.com
      paths:
        - path: /
          pathType: Prefix
# values.yaml — CKAN listener tuning and image-provided plugins
ckan:
  siteUrl: 'https://data.example.com'
  existingSecret: ckan-secrets
  containerPort: 5500
  uwsgiProcesses: 4
  uwsgiHarakiri: 120
  # Only enable plugins installed in the selected image.
  plugins: 'envvars image_view text_view datatables_view'

postgresql:
  enabled: true
  auth:
    password: 'strong-db-password'

redis:
  enabled: true
  auth:
    password: 'strong-redis-password'

datapusher:
  enabled: false
# values.yaml — CKAN with daily pg_dump backup
# Note: backup covers the ckan database only; uploaded files in /var/lib/ckan (PVC)
# are NOT included. Back up the PVC separately using Velero or storage snapshots.
ckan:
  siteUrl: 'https://data.example.com'
  existingSecret: ckan-secrets

postgresql:
  enabled: true
  auth:
    password: 'strong-db-password'

redis:
  enabled: true
  auth:
    password: 'strong-redis-password'

backup:
  enabled: true
  schedule: '0 3 * * *'
  s3:
    endpoint: https://s3.amazonaws.com
    bucket: ckan-backups
    existingSecret: ckan-s3-credentials

ingress:
  enabled: true
  ingressClassName: traefik
  hosts:
    - host: data.example.com
      paths:
        - path: /
          pathType: Prefix

Configuration Reference

Image

Parameter Type Default Description
image.repository string docker.io/ckan/ckan-base CKAN image.
image.tag string "2.12.0" Image tag.

CKAN Application

Parameter Type Default Description
ckan.containerPort integer 5000 Real uWSGI listener port.
ckan.uwsgiProcesses integer 2 uWSGI worker processes.
ckan.uwsgiHarakiri integer 60 Maximum request duration in seconds.
ckan.siteUrl string http://localhost:5000 Required. Full public URL (breaks all links if wrong).
ckan.siteTitle string CKAN Portal display name.
ckan.sysadminName string admin Sysadmin username.
ckan.sysadminEmail string [email protected] Sysadmin email.
ckan.sysadminPassword string "" Sysadmin password. Auto-generated if empty.
ckan.existingSecret string "" Existing secret with all three secrets.
ckan.existingSecretPasswordKey string sysadmin-password Key for sysadmin password.
ckan.existingSecretSessionKey string session-secret Key for Beaker session secret.
ckan.existingSecretJwtKey string jwt-secret Key for JWT secret.
ckan.plugins string envvars image_view text_view ... Space-separated list of active CKAN plugins.
ckan.replicaCount integer 1 CKAN web pod replicas.
ckan.extraEnv array [] Extra environment variables.

DataPusher

Parameter Type Default Description
datapusher.enabled boolean true Deploy DataPusher; external DataStore is still required.
datapusher.replicaCount integer 1 DataPusher pod replicas.
datapusher.port integer 8800 DataPusher service port.

Solr

Do not share Solr with other applications — CKAN uses a custom schema

The bundled Solr uses the ckan/ckan-solr image with a CKAN-specific schema. Connecting another application to this Solr instance may overwrite the schema and break CKAN’s search indexing.

Parameter Type Default Description
solr.enabled boolean true Deploy the bundled CKAN-specific Solr StatefulSet.
solr.port integer 8983 Real Solr listener and Service port.
solr.persistence.size string 5Gi Solr PVC size.
solr.externalUrl string "" External Solr URL (when solr.enabled: false).

Database

Parameter Type Default Description
database.mode string subchart Mode: subchart or external.
database.external.host string "" External PostgreSQL hostname.
database.external.ckanDatabase string ckan CKAN main database name.
database.external.datastoreDatabase string datastore DataStore extension database name.
database.external.datastoreReadUsername string datastore_ro Read-only user for the DataStore API.
database.external.existingSecret string "" Existing secret with database passwords.
database.external.existingSecretPasswordKey string password CKAN/DataStore write-role password key.
database.external.existingSecretDatastoreReadPasswordKey string datastore-read-password DataStore read-role password key.
database.external.sslMode string require libpq SSL mode; use verify-full with a trusted CA for certificate and hostname verification.
postgresql.enabled boolean true Deploy the bundled PostgreSQL subchart.
postgresql.startupProbe.initialDelaySeconds integer 30 Delay startup checks until PostgreSQL completes its temporary init server cycle.
postgresql.readinessProbe.initialDelaySeconds integer 30 Delay readiness checks until PostgreSQL completes its temporary init server cycle.
postgresql.auth.password string "" Password. Auto-generated if empty.

Redis

Parameter Type Default Description
redisConfig.mode string subchart Mode: subchart or external.
redisConfig.external.host string "" External Redis hostname.
redisConfig.external.port integer 6379 External Redis port.
redisConfig.external.database integer 0 Redis logical database.
redisConfig.external.tls boolean true Use rediss://.
redisConfig.external.sslCertReqs string required TLS certificate verification mode.
redisConfig.external.sslCaCerts string "" Mounted CA bundle path.
redisConfig.external.existingSecret string "" Secret containing the password.
redisConfig.external.existingSecretPasswordKey string redis-password Password key in the Secret.
redis.enabled boolean true Deploy the bundled Redis subchart.
redis.auth.password string "" Password. Auto-generated if empty.

Persistence and Service

Parameter Type Default Description
persistence.enabled boolean true Enable PVC for /var/lib/ckan (uploaded resources).
persistence.size string 10Gi PVC size.
service.port integer 80 Service port.
ingress.enabled boolean false Enable an Ingress resource.
ingress.ingressClassName string traefik Ingress class name.

service.port is independent from the process listener. Change ckan.containerPort to move uWSGI itself.

Network and identity

Parameter Type Default Description
serviceAccount.create boolean true Create a dedicated ServiceAccount.
serviceAccount.automountServiceAccountToken boolean false Mount Kubernetes API credentials.
networkPolicy.enabled boolean true Create component-aware NetworkPolicy rules.
networkPolicy.ingressFrom array [] Sources allowed to access the CKAN listener.
networkPolicy.egress array [{}] Complete egress rule list; replace per environment.

Backup

Backup covers the ckan database only — uploaded files in /var/lib/ckan are not included

The S3 backup CronJob runs pg_dump on the CKAN PostgreSQL database. Dataset files and resource uploads stored in the /var/lib/ckan PVC are not included in the backup. Use Velero, NFS snapshots, or storage provider snapshots to protect the PVC data.

Parameter Type Default Description
backup.enabled boolean false Enable scheduled pg_dump S3 backup.
backup.schedule string "0 3 * * *" Cron schedule.
backup.s3.endpoint string "" S3-compatible endpoint URL.
backup.s3.bucket string "" Target bucket name.
backup.s3.existingSecret string "" Existing secret with S3 credentials.
extraManifests array [] Extra Kubernetes manifests.

Upgrade Notes

CKAN 2.12.0 is a minor application upgrade. Back up the CKAN database and persistent storage, review the upstream 2.12.0 release, and test extensions against CKAN 2.12 before production rollout. Keep Solr, PostgreSQL, and Redis available while CKAN performs its upgrade steps. The former redisConfig.external.url value was replaced with structured connection fields and a Secret-backed password. The structured external Redis configuration enables TLS by default. Operators migrating a plaintext redis:// endpoint must set redisConfig.external.tls: false; TLS-enabled endpoints can retain the default.

More Information