## Intro

Running a Docker Registry in production means you are responsible for the integrity and availability of the images your team depends on. A lost registry can halt CI/CD pipelines, block deployments, and leave you scrambling to rebuild artifacts that may no longer exist elsewhere. Backup and restore are not optional add-ons; they are core operational tasks.

This guide walks through Docker Registry backup and restore using the official `registry:2` image and filesystem-based storage. You will learn how to inspect your environment, safely back up data, verify backups, restore after failure, and automate the process. Every command is explained with expected output and recovery decisions, so you can apply these steps with confidence.

We focus on practical, version-scoped procedures that minimize risk. Before changing anything, observe the current state. Use placeholders instead of secrets. After any change, verify the result. This is operational safety in action.

## Version and Environment Inventory

Begin by knowing exactly what you have. The official Docker Registry image is `registry:2`, and its data lives in `/var/lib/registry` inside the container. Most production setups run it with Docker Compose or Kubernetes, but the core concepts apply broadly.

First, confirm the container is running and note its configuration:

```bash
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
```

Expected output might look like:

```
NAMES       STATUS         PORTS
registry    Up 3 hours     0.0.0.0:5000->5000/tcp
```

Next, inspect the container to see its mounts, environment variables, and health status:

```bash
docker inspect registry
```

Look for the `Mounts` section. If you see a named volume or bind mount for `/var/lib/registry`, your data is already persistent. If not, your registry is writing to the container filesystem and will lose everything when the container is removed.

Check recent logs for any errors or warnings:

```bash
docker logs registry --tail 100
```

If using Docker Compose, the equivalent commands are:

```bash
docker compose ps
docker compose logs -f registry
docker compose exec registry sh
```

The last command gives you a shell inside the container to inspect the filesystem directly. Run `df -h /var/lib/registry` to see disk usage and ensure the volume has enough space for backups.

### Confirming Data Persistence

At this point, you must confirm where registry data is stored. A named volume like `registry_data:/var/lib/registry` is managed by Docker and survives container recreation. A bind mount like `./data:/var/lib/registry` maps directly to a host directory, which is easier to inspect and back up but can cause permission issues on different machines.

To test persistence, perform a restart test:

1. Stop the container: `docker stop registry`
2. Start it again: `docker start registry`
3. Pull an image list from the registry: `curl -s http://localhost:5000/v2/_catalog`

If the list still shows your images, the data persisted. If it is empty, your volume isn't mounted correctly. Fix the mount before proceeding.

## Safe Configuration Path

The safest way to back up a Docker Registry is to avoid interfering with running write operations. For a filesystem-backed registry, the recommended approach is to place the registry in read-only mode before copying data. This prevents corruption from concurrent writes.

### Step 1: Put Registry in Read-Only Mode

You can enable read-only mode by setting the `REGISTRY_STORAGE_MAINTENANCE_READONLY` environment variable to `enabled` and recreating the container. With Docker Compose, add to your service:

```yaml
services:
  registry:
    image: registry:2
    environment:
      REGISTRY_STORAGE_MAINTENANCE_READONLY: enabled
    volumes:
      - registry_data:/var/lib/registry
```

Then run:

```bash
docker compose up -d
```

Verify the registry is read-only by attempting to push an image. It should fail with a 405 error. For example:

```bash
docker push localhost:5000/test:latest
```

Expected output includes:

```
The push refers to repository [localhost:5000/test]
...
error: failed to push image: received unexpected HTTP status: 405 Method Not Allowed
```

### Step 2: Back Up the Data Directory

Now that the registry is stable, copy the data. If using a named volume, you can use a temporary container to export the data:

```bash
docker run --rm -v registry_data:/data -v $(pwd):/backup alpine tar czf /backup/registry-backup-$(date +%Y%m%d%H%M).tar.gz -C /data .
```

This command creates a compressed archive in your current directory with a timestamp. If using a bind mount, you can simply tar the host directory:

```bash
tar czf registry-backup-$(date +%Y%m%d%H%M).tar.gz -C ./data .
```

Check the archive size and list its contents to confirm it includes data:

```bash
ls -lh registry-backup-*.tar.gz
tar tzf registry-backup-*.tar.gz | head
```

You should see files like `docker/registry/v2/repositories/...`.

### Step 3: Return Registry to Read-Write Mode

After the backup completes, revert the read-only setting and restart the registry:

```yaml
environment:
  REGISTRY_STORAGE_MAINTENANCE_READONLY: disabled
```

Then run `docker compose up -d` again. Confirm normal operation by pushing a test image.

## Verification and Diagnostics

A backup is only useful if it can be restored. Verification reduces the chance that your backup is incomplete or corrupt.

### Verify Backup Integrity

One quick check is to compare file counts and sizes between the original and backup. While the registry is read-only, run:

```bash
find /var/lib/registry -type f | wc -l
```

(If using a named volume, you may need to exec into a container with that volume mounted.) Then compare with the backup:

```bash
tar tzf registry-backup-*.tar.gz | wc -l
```

The numbers should match exactly. A mismatch suggests files changed during backup or an incomplete archive.

### List Repositories After Restore

After restoring to a separate test registry, query the catalog:

```bash
curl -s http://localhost:5000/v2/_catalog
```

Expected output:

```json
{"repositories":["myapp","nginx","redis"]}
```

Compare this with the original registry's catalog. If any repository is missing, the backup may have been taken from the wrong directory or ignored hidden files.

### Diagnose Common Issues

If the registry fails to start after restore, check logs:

```bash
docker logs registry
```

Common errors include permission denials (fix ownership: `chown -R 1000:1000 /path/to/data` on host, because the registry container runs as UID 1000) and corrupted storage (indicated by `level=error msg="error resolving image"`). In such cases, restore from an earlier backup.

## Failure Modes and Recovery

Registries fail in predictable ways. Knowing these modes helps you respond quickly and accurately.

### Failure Mode 1: Container Deleted Accidentally

If you have a volume mount, the data survives. Simply recreate the container with the same volume:

```bash
docker run -d -p 5000:5000 --name registry -v registry_data:/var/lib/registry registry:2
```

The registry will serve the same images. If you had no volume, you must restore from backup.

### Failure Mode 2: Host Disk Failure

If the host disk containing the registry data fails, you need to restore the backup to a new disk on a new or repaired host. After provisioning the host:

1. Recreate the directory structure and set permissions.
2. Extract the backup: `tar xzf registry-backup-*.tar.gz -C /new/data/path`
3. Mount that directory into a new registry container and start it.

### Failure Mode 3: Corrupted Storage

Storage corruption can happen due to abrupt power loss or disk errors. Symptoms include 500 errors on pulls and log messages like `filesystem: file has vanished`. To recover, stop the registry, replace the data directory with the latest backup, and restart. If corruption recurs, investigate disk health immediately.

### Failure Mode 4: Incomplete Backup

If a backup was taken while the registry was writable, some blobs may be missing. You can detect this by comparing the backup size with the live data size. Restore may fail partially; in that case, use an older backup and re-push any images that were added after that backup.

### Recovery Drill

Run a recovery drill quarterly. Simulate a failure by moving the live data directory aside, restoring from backup, and verifying that `docker pull localhost:5000/myapp:latest` works. Document the exact steps and time required. This ensures your team can execute recovery under pressure.

## Operations Checklist

Use this checklist to maintain a robust backup strategy. Assign a single owner, and review the checklist monthly.

| Step | Action | Owner | Frequency | Expected Success Signal |
|------|--------|-------|-----------|-------------------------|
| 1 | Verify volume mount is correct | Priya Shah, DevOps Lead | Monthly | `docker inspect` shows mount at `/var/lib/registry` |
| 2 | Run backup script | Marcus Chen, SysAdmin | Daily | Archive file created with timestamp, size > 1 MB |
| 3 | Verify backup integrity (file counts match) | Marcus Chen | Weekly | Counts match exactly |
| 4 | Test restore to staging registry | Priya Shah | Monthly | Catalog matches production, pulls succeed |
| 5 | Rotate old backups | Marcus Chen | Monthly | Only last 6 daily and 4 weekly backups retained |
| 6 | Monitor disk space | Automated alert | Continuous | Alert if volume > 80% full |
| 7 | Update documentation with any changes | Priya Shah | As needed | Document reflects current setup |

Each step must have a clear owner, not a team. If Marcus is unavailable, Priya is his backup, but accountability remains with the named owner.

## Common Pitfalls and How to Avoid Them

### Pitfall 1: Assuming Default Storage is Persistent

Many first-time users run `docker run -d -p 5000:5000 registry:2` without a volume. The registry stores data on the container's ephemeral filesystem. When the container is removed, all images are lost.

**How to avoid:** Always define a volume in your `docker run` or compose file. Check with `docker inspect` that the mount is present.

### Pitfall 2: Taking Backups While Registry Is Writable

Backing up a live filesystem can result in inconsistent state: an image manifest is saved but its blobs are not, or vice versa. This leads to restore failures.

**How to avoid:** Enable read-only mode before backup, as described above. If you cannot afford downtime, use filesystem snapshots (e.g., LVM, ZFS) for atomic backups, then copy from the snapshot.

### Pitfall 3: Ignoring Permissions on Restore

The registry container runs as UID 1000. If you restore files on the host and they are owned by root, the registry cannot read them, resulting in permission errors.

**How to avoid:** After extraction, run `chown -R 1000:1000 /path/to/registry/data` before starting the container.

### Pitfall 4: Not Testing Restores

A backup that has never been restored is a hope, not a backup. Corrupt archives, missing files, or changed configurations may only surface during an actual emergency.

**How to avoid:** Automate a restore test to a staging registry on a schedule. Compare catalogs and pull a set of representative images.

### Pitfall 5: Backing Up Only the Data Directory, Forgetting Configuration

If you customize the registry configuration (e.g., authentication, storage backends), those settings are not in the data directory. Losing them means your restored registry may behave differently.

**How to avoid:** Store your `config.yml` and compose files in version control. Include them in the backup archive or ensure they are recoverable from Git.

### Pitfall 6: Relying on a Single Backup Copy

If the backup resides on the same disk as the registry, a disk failure destroys both. If it is only on one external drive, that drive could fail.

**How to avoid:** Follow the 3-2-1 rule: three copies, two different media, one offsite. Use cloud object storage for offsite copies.

## Conclusion

Docker Registry backup and restore is a critical discipline that protects your software supply chain. By understanding your environment, implementing read-only backups, verifying integrity, and rehearsing recovery, you can prevent data loss and minimize downtime.

Start with a low-risk action: inspect your current registry setup. Is the data on a persistent volume? If not, fix that today. Then implement the backup script in this guide, test a restore, and set up monitoring. These steps move you from reactive panic to proactive resilience.

A reliable workflow makes failure visible, protects sensitive values, limits changes to the intended resource, and defines recovery verification before an incident forces the decision. Assign clear owners, review the checklist monthly, and keep your team prepared for the day you need to use that backup.