Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

Backing up and restoring Pulp (container and Kubernetes)

By updated

Improve this page

Pulp logo

This guide backs up and restores a Pulp instance without losing anything, with podman containers as well as on Kubernetes, and also shows how to export the content of a single repository to serve it again elsewhere. Intended audience: administrators responsible for the continuity of an artifact repository. Prerequisites: a running Pulp instance. The point to understand before anything else: with Pulp, data is spread across three distinct locations, and backing up one without the others gives a broken restore.

Many people think that "everything is in the database". That is wrong, and the mistake is costly on restore day. Understanding where the data lives is the prerequisite for any reliable backup strategy.

What you will learn

  • Identify the three data locations of Pulp.
  • Back up a container instance (database, artifacts, keys).
  • Restore in the right order to avoid dead links.
  • Automate backup and restore on Kubernetes.
  • Export the content of a repository, and import it on another instance.

Where the data lives: three locations, not one

Pulp does not store everything in the same place. A complete backup must cover all three:

LocationContentWithout it, at restore time
PostgreSQLMetadata: repositories, versions, publications, distributions, tasks, RBACNo structure left: the artifacts exist but nothing knows how to serve them
File storage (/var/lib/pulp/media, or S3 object storage)The artifacts themselves (binaries, addressed by sha256)The database points to missing files: dead links
Encryption keys (/etc/pulp/certs)The key that encrypts sensitive fields (remote credentials) and the secret keyThe encrypted credentials are unrecoverable

The database therefore holds metadata only. The binaries live separately, on a file system or in object storage. Redis only serves as an ephemeral task queue and does not need to be backed up.

Backing up a container instance

In a podman deployment, the three locations are mounted volumes. The backup consists of freezing the database and archiving the volumes.

  1. Dump the PostgreSQL database. The text dump is portable:

    Terminal window
    podman exec pulp bash -c 'pg_dump -U pulp pulp' > pulp-db.sql

    The output should produce a file of several thousand lines starting with -- PostgreSQL database dump.

  2. Archive the artifacts from the storage volume:

    Terminal window
    podman exec pulp tar czf - -C /var/lib/pulp media > pulp-artifacts.tar.gz
  3. Copy the encryption and session keys:

    Terminal window
    podman exec pulp tar czf - -C /etc/pulp certs > pulp-certs.tar.gz

These three files (pulp-db.sql, pulp-artifacts.tar.gz, pulp-certs.tar.gz) form a complete and consistent backup. Store them together, encrypted, off the Pulp machine.

Restoring a container instance, in the right order

The restore order is not arbitrary. You restore the artifacts and the keys first, and the database last.

  1. Restore the keys and the artifacts into the volumes of the new instance:

    Terminal window
    tar xzf pulp-certs.tar.gz -C /chemin/volume/etc-pulp
    tar xzf pulp-artifacts.tar.gz -C /chemin/volume/var-lib-pulp
  2. Reload the database:

    Terminal window
    podman exec -i pulp bash -c 'psql -U pulp pulp' < pulp-db.sql
  3. Check that the API responds and that the repositories are there:

    Terminal window
    pulp status
    pulp file repository list

The logic is simple: a restored database that points to a missing artifact breaks the service, whereas an artifact present without a database record is simply orphaned, hence harmless and cleaned up later by the orphan cleanup. So you put the immutable data first.

On Kubernetes: PulpBackup and PulpRestore

The operator automates the whole cycle through two custom resources. You describe a backup, and the operator captures the database, artifacts and secrets to a dedicated PVC; a restore resource recreates the complete instance from that PVC.

Backing up with PulpBackup

  1. Create a destination PVC for the backup:

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: pulp-backup-claim
    namespace: pulp
    spec:
    accessModes: ["ReadWriteOnce"]
    storageClassName: local-path
    resources:
    requests:
    storage: 5Gi
  2. Declare the backup with a PulpBackup resource:

    apiVersion: repo-manager.pulpproject.org/v1
    kind: PulpBackup
    metadata:
    name: sauvegarde-pulp
    namespace: pulp
    spec:
    deployment_name: pulp
    backup_pvc: pulp-backup-claim
    Terminal window
    kubectl apply -f pulpbackup.yaml

    The operator launches a job which, once finished, sets the BackupComplete condition to True. The PVC then contains a timestamped folder with the pulp.db dump, the cr_object (the instance definition) and all the secrets, including db_fields_encryption_secret.yaml (the encryption key) and pulp_secret_key.yaml.

Restoring with PulpRestore

The restore is declared just as declaratively. It recreates the complete instance and reloads the database and secrets from the backup PVC.

apiVersion: repo-manager.pulpproject.org/v1
kind: PulpRestore
metadata:
name: restauration-pulp
namespace: pulp
spec:
deployment_name: pulp
backup_name: sauvegarde-pulp
backup_pvc: pulp-backup-claim
Terminal window
kubectl apply -f pulprestore.yaml

The backup_name field is mandatory: it designates the PulpBackup resource to restore. The operator then recreates the database, the API, the content, the workers and the reverse proxy, then reloads the dump and the secrets. Next, check that your repositories are back:

Terminal window
kubectl -n pulp port-forward svc/pulp-api-svc 24817:24817 &
curl -s -u admin:MOT_DE_PASSE \
http://localhost:24817/pulp/api/v3/repositories/file/file/

Backing up the content of a repository: export and import

The two previous methods back up the machine. Pulp can also back up the content of a repository, independently of the instance that hosts it: this is export, and it is what you need when the goal is to serve again elsewhere rather than to rebuild the same instance.

The two levels do not replace each other. An export contains neither the accounts, nor the remotes, nor the encryption key: it contains artifacts and the metadata that describe them. It is restored on a new instance, or alongside, in another repository.

Allowing the paths, and restarting only once

Pulp only exports to a directory listed in ALLOWED_EXPORT_PATHS, and only imports from a directory listed in ALLOWED_IMPORT_PATHS. Without these two settings, creating the exporter fails with a message that leaves no doubt:

Error: {"path":["Path '/var/lib/pulp/exports' is not an allowed export path"]}

These settings are read at startup only. So set both at once, then restart a single time:

ALLOWED_EXPORT_PATHS = ["/var/lib/pulp/exports"]
ALLOWED_IMPORT_PATHS = ["/var/lib/pulp/imports"]

Exporting, then getting the file off the machine

The exporter is an object that you create once; the export is an operation that you rerun each time. The two have distinct subcommands, and this is the most frequent trap: pulp exporter for the object, pulp export for the operation, which expects --exporter and not --name.

Terminal window
pulp exporter pulp create --name secours \
--path /var/lib/pulp/exports \
--repository python:python:interne
pulp export pulp run --exporter secours

The operation produces two inseparable files: a .tar archive and its -toc.json table of contents. One without the other restores nothing.

And above all, get them off the machine. An export left in /var/lib/pulp/exports disappears with the disk it was meant to protect. This is the most widespread and the most costly mistake: you discover on the day of the outage that the backup was inside the outage.

Importing: the operation the CLI cannot launch

Verified on pulp-cli 0.41.1: pulp importer pulp only exposes create, destroy, list, show and update, and pulp import does not exist. The Pulp documentation only shows the operation through the API, so that is the only way.

Three points to know before you start:

  • the destination repository must exist and be empty; the import fills it, it does not create it;
  • the --repo-map option takes two arguments (source name, target name), not JSON;
  • the path of the -toc.json is the one the container sees (/var/lib/pulp/imports/...), never the host's.
Terminal window
pulp python repository create --name restaure
pulp importer pulp create --name secours --repo-map interne restaure
IMP=$(pulp --format json importer pulp show --name secours \
| python3 -c "import sys,json;print(json.load(sys.stdin)['pulp_href'])")
curl -u admin:MOT_DE_PASSE -X POST \
-H 'Content-Type: application/json' \
-d '{"toc": "/var/lib/pulp/imports/export-....-toc.json"}' \
"http://localhost:8080${IMP}imports/"

The response returns a task_group and not a task: the import starts several tasks, one per imported resource. Monitoring written for a single task would wait forever for an end it will never see.

Once the import is finished, the restored repository must be published and distributed to serve: a filled repository with no distribution serves nobody.

Hands-on practice

The lab has you export a repository, then import it into an empty repository and serve a client with what was restored. Its central check is neither the export nor the import: it is that the backup sits on another machine. An export left in /var/lib/pulp/exports disappears with the disk it was meant to protect, and this is the most common outage. The lab also makes you run into the four refusals of Pulp before the first successful export, including the path seen from the container rather than from the host.

Key takeaways

  • Pulp data lives in three places: database (metadata), file storage (artifacts), encryption keys.
  • The database does not contain the artifacts: the binaries are on a file system or in object storage.
  • The database_fields.symmetric.key key is essential: without it, the encrypted credentials of the remotes are lost.
  • In a container, you restore artifacts and keys first, database last (artifacts are immutable and addressed by digest).
  • On Kubernetes, PulpBackup / PulpRestore automate the full cycle; the database must run PostgreSQL 15 for the backup to work.
  • Export backs up the content of a repository, not the machine: it is restored on a new instance, but contains no accounts, no remotes and no keys.
  • An export produces two inseparable files, the archive and its -toc.json, and it is only worth anything once off the machine it protects.
  • Import can only be launched through the API: the CLI does not expose it, and the response returns a task_group, not a task.

Knowledge check

Eight questions on what a backup must cover: the three places the data lives, the key that decides whether syncs survive, and the order that avoids dead links.

Knowledge check

Check what you have learned with this interactive quiz

8 questions
8 min
70% to pass

How it works

  • The timer starts when you click Start
  • Multiple choice, true/false and short answer questions
  • You can move between questions
  • Detailed results are shown at the end

Starts the quiz and the timer

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn