Skip to content

Webspace Restore Guide

This guide documents the process for restoring a webspace (WordPress site) from a restic backup, including database and files.

Prerequisites

  • Access to the Kubernetes cluster
  • Restic backup containing the webspace data
  • The webspace's database credentials secret must exist
  • MariaDB operator installed in the cluster
  • A MariaDB instance named db must exist in the webspaces namespace

Restore Process

Replace $NAME with the webspace name (e.g., vefa) throughout this guide.

Step 1: Suspend Flux HelmRelease

bash
flux suspend -n webspaces helmrelease $NAME

Step 2: Scale Deployment to 0 Replicas

bash
kubectl scale --replicas=0 -n webspaces deploy/${NAME}-webserver

Step 3: Create Alpine Restore Pod

Create and apply a pod manifest that mounts the PVC and provides database credentials:

bash
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: ${NAME}-restore
  namespace: webspaces
spec:
  containers:
  - name: alpine
    image: alpine:latest
    command: ["/bin/sh"]
    args: ["-c", "while true; do sleep 3600; done"]
    volumeMounts:
    - name: data
      mountPath: /data
    env:
    - name: DB_HOST
      value: "db.webspaces.svc.cluster.local"
    - name: DB_USER
      value: "${NAME}"
    - name: DB_NAME
      value: "${NAME}"
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: ${NAME}-credentials
          key: db-password
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: ${NAME}-files
  restartPolicy: Never
EOF

kubectl wait --for=condition=Ready pod/${NAME}-restore -n webspaces --timeout=60s

Step 4: Install Tools and Configure Restic

Exec into the pod:

bash
kubectl exec -it ${NAME}-restore -n webspaces -- /bin/sh

Install required tools:

bash
apk add --no-cache restic mariadb-client

Set restic environment variables:

bash
export RESTIC_REPOSITORY='s3:https://YOUR_S3_ENDPOINT/YOUR_BUCKET'
export RESTIC_PASSWORD='YOUR_RESTIC_PASSWORD'
export AWS_ACCESS_KEY_ID='YOUR_ACCESS_KEY'
export AWS_SECRET_ACCESS_KEY='YOUR_SECRET_KEY'

Step 5: Identify Restic Snapshot

List snapshots and find the one to restore:

bash
restic snapshots

Inspect the snapshot to verify contents:

bash
restic ls SNAPSHOT_ID

Look for:

  • SQL dump file (e.g., ${NAME}.sql or ${NAME}.sql.gz)
  • Website files directory (e.g., ${NAME}-files/)

Step 6: Restore Files from Restic

First, inspect the snapshot to find the paths:

bash
restic ls SNAPSHOT_ID | head -20

Restore only the SQL dump and WordPress files directory directly to /data:

bash
# Replace with actual paths from your snapshot
restic restore SNAPSHOT_ID --target /data --include "/path/to/${NAME}.sql*" --include "/path/to/${NAME}-files/**"

Note: You may see SELinux xattr warnings - these can be safely ignored as long as the summary shows files were restored.

Step 7: Restore SQL Dump

Find the SQL dump file:

bash
find /data -name "*.sql" -o -name "*.sql.gz"

Import the database:

bash
# For uncompressed SQL dump
mariadb -h db -u $DB_USER -p$DB_PASSWORD $DB_NAME < /path/to/dump.sql

# For compressed SQL dump
gunzip -c /path/to/dump.sql.gz | mariadb -h db -u $DB_USER -p$DB_PASSWORD $DB_NAME

Verify the import:

bash
mariadb -h db -u $DB_USER -p$DB_PASSWORD $DB_NAME -e "SHOW TABLES;"
mariadb -h db -u $DB_USER -p$DB_PASSWORD $DB_NAME -e "SELECT COUNT(*) FROM wp_posts;"

Note: The Database may have another prefix than wp_. In that case, see what tables show up when running the first command.

Step 8: Finalize PVC

Move website files to /data root and clean up:

bash
# Move website files to root (adjust path based on your backup structure)
mv /data/path/to/${NAME}-files/* /data/
mv /data/path/to/${NAME}-files/.* /data/ 2>/dev/null || true

# Remove backup directory structure and SQL dump
rm -rf /data/path/to
rm -f /data/*.sql /data/*.sql.gz

Verify files are in the correct location:

bash
ls -la /data/ | head -20

You should see WordPress files like:

  • index.php
  • wp-config.php
  • wp-content/
  • wp-includes/

Step 9: Update wp-config.php

Important: WordPress configuration files often have hardcoded database credentials that may be outdated.

Check current configuration:

bash
grep "DB_HOST\|DB_PASSWORD" /data/wp-config.php | grep define

Update to use environment variables:

bash
sed -i "s/define([[:space:]]*'DB_HOST',[[:space:]]*'[^']*'[[:space:]]*);/define( 'DB_HOST', getenv('WORDPRESS_DB_HOST') );/" /data/wp-config.php
sed -i "s/define([[:space:]]*'DB_PASSWORD',[[:space:]]*'[^']*'[[:space:]]*);/define( 'DB_PASSWORD', getenv('WORDPRESS_DB_PASSWORD') );/" /data/wp-config.php

Verify changes:

bash
grep "DB_HOST\|DB_PASSWORD" /data/wp-config.php | grep define

Note on Hardcoded Secrets: Many restored WordPress installations have hardcoded database passwords in wp-config.php. This is a security concern because:

  • Passwords are stored in plain text in the file
  • They may not match the current Kubernetes secrets
  • Changing secrets requires manual file updates
  • Secrets are visible in backups and file system

Recommended Solution: Configure WordPress to read database credentials from environment variables (as shown above). The webspace helm chart already injects these variables into the PHP container:

  • WORDPRESS_DB_HOST
  • WORDPRESS_DB_NAME
  • WORDPRESS_DB_USER
  • WORDPRESS_DB_PASSWORD

Using getenv() in wp-config.php allows centralized secret management through Kubernetes secrets.

Step 10: Exit and Delete Restore Pod

bash
exit  # Exit the pod shell
kubectl delete pod ${NAME}-restore -n webspaces

Step 11: Scale Deployment Back Up

bash
kubectl scale --replicas=1 -n webspaces deploy/${NAME}-webserver

Step 12: Resume Flux HelmRelease

bash
flux resume -n webspaces helmrelease $NAME

Step 13: Verify Deployment

Check that pods are running:

bash
kubectl get pods -n webspaces | grep ${NAME}

Test the website in a browser to ensure it loads correctly.