Extracting a Single File from a Borg Archive Without Recreating the Entire Directory Tree
When a backup archive grows to several terabytes, restoring a single configuration file or a database dump can feel like pulling a needle from a haystack. Borg’s default extract command rebuilds the whole directory tree, which is wasteful when you only need one file. The following guide shows how to pull a single file efficiently, keeps metadata intact, and keeps your system secure.
Why Pull a Single File?
- Time‑saving – Restoring a 1 GB file from a 50 GB archive can take minutes if the whole tree is rebuilt.
- Disk‑space conservation – You avoid creating thousands of empty directories.
- Audit and forensic work – Quickly inspect a file without touching the rest of the backup.
- Recovery scripts – Automate extraction of log files or config snippets during incident response.
Quick Borg Reference
| Command | Purpose | Typical flags |
|---|---|---|
borg list | Show archive contents | --format |
borg extract | Restore files | --strip-components, --no-same-permissions |
borg cat | Output a file’s contents | --format |
borg export | Stream a file to stdout | --format |
All commands assume a local repository. For remote repos, prepend ssh://user@host:/path/to/repo or use the --remote-path option.
1. Locate the File Inside the Archive
Before extracting, you need the exact path as stored in the archive. Use borg list with a format that shows the full path:
borg list /mnt/backup::my‑archive \
--format="{path}\n" | grep -i "myapp/config.yaml"
If the archive contains multiple matching files, add --sort or --limit to narrow the output. The result will look like:
home/user/projects/myapp/config.yaml
Keep this path; it will be the argument for the extraction commands.
2. Extracting With borg extract
The simplest way to pull a file while avoiding the rest of the tree is to use --strip-components. This option removes a specified number of leading path components before writing the file.
borg extract /mnt/backup::my‑archive \
--strip-components=3 \
home/user/projects/myapp/config.yaml
How It Works
home/user/projects/myapp/config.yaml→myapp/config.yamlafter stripping three components (home,user,projects).- The file is written to the current working directory (or a path you specify with
--target).
Trade‑offs
| Benefit | Drawback |
|---|---|
| Fast – only the file’s data blocks are read. | Path loss – you lose the original directory context unless you reconstruct it manually. |
| Low disk usage – no extra directories. | Permissions – the file inherits the current user’s UID/GID unless you use --same-permissions. |
If you need the original ownership and permissions, add --same-permissions and ensure the target directory is owned by the same UID/GID as the backup.
3. Using borg cat for Raw Extraction
When you only need the file’s contents (e.g., to pipe into another tool), borg cat is the most lightweight option:
borg cat /mnt/backup::my‑archive \
--format="{path}\n" \
home/user/projects/myapp/config.yaml > /tmp/config.yaml
borg cat streams the file directly to stdout, so you can redirect it anywhere. This is ideal for:
- Feeding a file into
opensslfor decryption. - Piping into
jqfor JSON manipulation. - Feeding into
sedorawkfor quick edits.
Caveat: borg cat does not preserve file metadata. If you need ownership or timestamps, use extract.
4. Streaming with borg export
borg export is a newer command that streams a file or directory to stdout, preserving metadata in the stream. It’s handy when you want to pipe the file into another program that accepts a file descriptor.
borg export /mnt/backup::my‑archive \
home/user/projects/myapp/config.yaml | \
gzip > /tmp/config.yaml.gz
The exported stream contains the file’s mode, timestamps, and extended attributes. The receiving side can use borg import to restore the file with metadata intact.
5. Working With Remote Repositories
When the repository is on a remote host, the same commands apply, but you must specify the remote path. For example:
borg extract ssh://backupuser@backup.example.com:/srv/backup::my‑archive \
--strip-components=3 \
home/user/projects/myapp/config.yaml
If the remote host runs a non‑standard borg binary, use --remote-path:
borg extract --remote-path=/usr/local/bin/borg \
ssh://backupuser@backup.example.com:/srv/backup::my‑archive \
--strip-components=3 \
home/user/projects/myapp/config.yaml
Security note: The SSH key used for the connection should have the no-pty and command="borg serve" restrictions in authorized_keys to prevent arbitrary command execution. See the Borg documentation for a recommended authorized_keys snippet.
6. Preserving Permissions and Ownership
If you need the file to retain its original UID/GID, use --same-permissions and --same-owner (the latter requires root). Example:
sudo borg extract /mnt/backup::my‑archive \
--strip-components=3 \
--same-permissions \
--same-owner \
home/user/projects/myapp/config.yaml
Running as root is necessary because changing ownership to a non‑existing UID/GID is not permitted otherwise. If the target system has the same user accounts, the file will be restored with the correct ownership.
7. Security Considerations
| Issue | Mitigation |
|---|---|
| Encrypted archives | Always use Borg’s built‑in encryption (borg create --encryption=repokey). The key is stored in ~/.config/borg/keys. Keep the key file on a separate, encrypted device. |
| Key exposure | Use borg key export to rotate keys. Store the key in a password‑protected keyring (e.g., pass). |
| Remote access | Restrict SSH access to the backup user. Use authorized_keys with command="borg serve" and no-pty. |
See also
- Configure systemd’s OnFailure to email me when my daily backup service dies
- When chmod 2775 Turns Into a Security Hole: Fixing Setgid Misconfigurations on /srv/shared
- How I Stopped Debian from Installing KDE Plasma During a System Upgrade – A Practical APT Pinning Example
- Fixing broken /etc/hosts entries after a Windows sync introduces stray CR characters
- Why Your systemd Service Stays “activating” After a Kernel Upgrade – A Step‑by‑Step Fix