Migrating from Bitnami to Official PostgreSQL Images with the Nextcloud Community Chart

· JW
cover

Bitnami Helm Charts have moved to a subscription model that is significantly out of my budget so I had to do some cleanup in my Homelab cluster to get rid of any Bitnami images that I was using.

Fortunately it wasn't many. I was using the Bitnami chart for my centralized Redis instance, but that was easy enough to swap out with the official image. But the Nextcloud Community Chart uses Bitnami images for databases, so I needed to move away from those.

First I disabled the Bitnami Redis instance included in the Nextcloud helm chart and pointed it towards my new Redis server in the values.yaml.

redis:
  enabled: false
externalRedis:
  enabled: true
  ## Redis host
  host: "redis-svc.redis.svc.cluster.local"
  ## Redis port
  port: "6379"
  ## Use an existing secret
  existingSecret:
    enabled: true
    secretName: nextcloud-redis-secrets
    passwordKey: password

The second database was Postgres, which was going to be a little bit of work. So let's see how I did it...

Prerequisites

  • Ability to execute into pods with kubectl
  • Nextcloud and Postgres instances running in the nextcloud namespace (you can modify commands to match your namespace if it's different)
  • A backup of all Nextcloud data before starting would also be a good idea

Step 1: Get the new instance of Postgres up and running

Setting up a Postgres instance in Kubernetes is quite easy. The only caveat here was that I had to prepare it for the Nextcloud migration, but that was simply setting a couple environment variables in the manifest. Here's how it looks:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nextcloud-postgresql
spec:
  selector:
    matchLabels:
      app: nextcloud-postgresql
  replicas: 1
  template:
    metadata:
      labels:
        app: nextcloud-postgresql
    spec:
      containers:
      - name: postgres
        image: postgres:17 # Use whatever version tag you want
        ports:
        - containerPort: 5432
          name: tcp-postgres
        
        env:
        - name: POSTGRES_DB # nextcloud database name
          value: nextcloud
        - name: POSTGRES_USER # nextcloud username
          valueFrom:
            secretKeyRef:
              name: nextcloud-db-secret
              key: username
        - name: POSTGRES_PASSWORD
          valueFrom:
            secretKeyRef:
              name: nextcloud-db-secret
              key: password
        
        - name: PGDATA # postgres data directory
          value: /var/lib/postgresql/data/pgdata
        
        volumeMounts:
        - name: nextcloud-postgres-data
          mountPath: /var/lib/postgresql/data

      volumes:
        - name: nextcloud-postgres-data
          persistentVolumeClaim:
            claimName: nextcloud-postgres-data
---
apiVersion: v1
kind: Service
metadata:
  name: nextcloud-postgresql-svc
  labels:
    app: nextcloud-postgresql
spec:
  ports:
  - port: 5432
    name: tcp-postgres
  selector:
    app: nextcloud-postgresql

You can see that I'm setting POSTGRES_USER and POSTGRES_PASSWORD from a secret that I already had created inside the nextcloud namespace, and just setting POSTGRES_DB to nextcloud.

The persistent data is also using a PVC named nextcloud-postgres-data created previously inside the nextcloud namespace.

tip
tip

If you don't have a secret already you can easily create one with this:

kubectl -n nextcloud create secret generic nextcloud-db-secret \
  --from-literal=username=nextcloud \
  --from-literal=password=my-totally-secure-password

And the service is just type ClusterIP named nextcloud-postgresql-svc

warning
warning

I'm only running a single instance of Postgres and don't need high-availability or scalability so I went with a Deployment. This is not recommended for a robust scalable production environment where you should use a StatefulSet with volumeClaimTemplates.

Once that was up and running it was time for the next step.

Step 2: Migrate the Postgres data

Put Nextcloud in maintenance mode

kubectl -n nextcloud exec -it <nextcloud-pod> -- php occ maintenance:mode --on

Dump the Postgres nextcloud database to local machine

kubectl -n nextcloud exec -it <bitnami-postgres-pod> -- pg_dump -U <nextcloud-username> -d <nextcloud-database-name> > nextcloud.sql

Copy the local nextcloud.sql to the postgres pod

kubectl -n nextcloud cp ./nextcloud.sql <new-postgres-pod>:/tmp/nextcloud.sql

Restore the database in the new Postgres pod

kubectl -n nextcloud exec -it <new-postgres-pod> -- psql -U <nextcloud-username> -d <nextcloud-database-name> -f /tmp/nextcloud.sql

Step 3: Point Nextcloud to the new Postgres database

I just edited the values.yaml to disable the Bitnami Postgres and use externalDatabase and pointed the details to my new Postgres service and secret

postgresql:
  enabled: false

externalDatabase:
  enabled: true
  type: postgresql
  host: nextcloud-postgresql-svc  # New instance's service name
  database: nextcloud
  existingSecret:
    enabled: true
    secretName: nextcloud-db-secret
    usernameKey: username
    passwordKey: password

After that you should be able to apply the Helm chart with the updated values and be good to go with the new database.

ArgoCD manages my helm deployments, but you may need to apply the helm chart with a command like this:

helm upgrade nextcloud nextcloud/nextcloud -f values.yaml -n nextcloud
failure
error: issue with Nextcloud's config.php

After applying the Helm chart I found that it did not update the values of the config.php to the new database service. This may be an issue with the Helm chart, the way the PVC is mounted, or maybe I didn't do something right.

Fixing the config.php values

I decided to just manually edit the config.php in the Nextcloud pod to the updated values. vi and nano weren't available, but sed was, so I used that to update

# create a backup first
kubectl -n nextcloud exec -it <nextcloud-pod> -- cp /var/www/html/config/config.php /var/www/html/config/config.php.BAK

# update dbhost
kubectl -n nextcloud exec -it <nextcloud-pod> -- sed -i "s/'dbhost' => '.*'/'dbhost' => 'nextcloud-postgresql-svc'/" /var/www/html/config/config.php

Replace nextcloud-postgresql-svc with whatever your service name for Postgres is.

Then I just recreated the pod, waited for it to report that it was up, and then went to the domain it's hosted at and saw that it was still up and in maintenance mode.

tip
tip

If you run into any errors here, check your nextcloud logs with:

kubectl -n nextcloud exec -it <nextcloud-pod> -- less /var/www/html/data/nextcloud.log

Step 4: Turn off maintenance mode

kubectl -n nextcloud exec -it <nextcloud-pod> -- php occ maintenance:mode --off

And with that you should be set up with your new Postgres instance that isn't using a Bitnami image.