Storage Box Plans and What You Actually Get

In 2026, Hetzner offers five Storage Box tiers: BX11 (1TB, €3.81/month), BX21 (2TB, €6.90/month), BX31 (5TB, €14.90/month), BX41 (10TB, €27.90/month), and BX61 (20TB, €52.90/month). All prices are net, VAT added depending on your country.

Every plan includes the same feature set regardless of tier: SFTP, FTP/FTPS, rsync over SSH, Samba, WebDAV, and Borg backup access. You also get up to 200 sub-accounts, each with their own credentials and optionally restricted to a subdirectory. Snapshots are available - up to 10 per box, retained for 7 days by default, configurable up to 14 days. External snapshots cost extra but give you point-in-time recovery without manual intervention.

Storage Box lives in Hetzner's Falkenstein, Nuremberg, or Helsinki data centers. You pick the location at order time and cannot move it later without data migration. If your servers run in Hetzner Cloud or on dedicated hardware in the same location, internal traffic between your server and Storage Box is not metered. That internal network bandwidth matters when you are pushing hundreds of gigabytes of nightly backups.

Initial Setup and SSH Key Authentication

After ordering through robot.hetzner.com, your Storage Box hostname follows the pattern `uXXXXXX.your-storagebox.de` where the number matches your robot account user ID. Credentials arrive by email, but you should disable password auth immediately and switch to SSH keys.

Generate a dedicated key pair for backup operations rather than reusing your personal key:

In the Hetzner Robot panel, go to Storage Box > SSH Keys > Add. Paste the public key. Hetzner supports RSA (minimum 2048-bit), ECDSA, and Ed25519. Use Ed25519.

Test the connection. Storage Box SSH only allows SFTP and rsync over SSH - you cannot get an interactive shell. The port is 23, not 22.

ssh-keygen -t ed25519 -C "backup@yourserver" -f ~/.ssh/storagebox_ed25519

# Test SFTP access
sftp -i ~/.ssh/storagebox_ed25519 -P 23 uXXXXXX@uXXXXXX.your-storagebox.de

# Test rsync over SSH
rsync -avz -e "ssh -i ~/.ssh/storagebox_ed25519 -p 23" /path/to/data/ \
  uXXXXXX@uXXXXXX.your-storagebox.de:/backups/

Mounting Storage Box as a Filesystem with SSHFS

SSHFS lets you mount Storage Box as a local directory, which is useful when legacy applications expect a local path or when you want to run tools like `du` or `find` against the remote storage. This is not appropriate for high-frequency random writes - it adds latency on every syscall.

Install sshfs on Debian/Ubuntu:

Create a mount point and set up the mount. The reconnect and ServerAliveInterval options matter for long-running processes that hold the mount across network interruptions.

For persistent mounts across reboots, add an entry to `/etc/fstab`. The `_netdev` flag tells systemd to wait for network before mounting.

Verify the mount worked and check available space:

apt install sshfs -y

mkdir -p /mnt/storagebox

# Mount command
sshfs -o IdentityFile=/root/.ssh/storagebox_ed25519 \
  -o Port=23 \
  -o reconnect \
  -o ServerAliveInterval=15 \
  -o ServerAliveCountMax=3 \
  -o allow_other \
  uXXXXXX@uXXXXXX.your-storagebox.de:/ /mnt/storagebox

# /etc/fstab entry
uXXXXXX@uXXXXXX.your-storagebox.de:/ /mnt/storagebox fuse.sshfs \
  IdentityFile=/root/.ssh/storagebox_ed25519,Port=23,_netdev,\
  reconnect,ServerAliveInterval=15,allow_other 0 0

# Check space
df -h /mnt/storagebox
// advertisement

Mounting via Samba for Mixed Environments

If you run a mixed Windows/Linux environment or need to expose Storage Box to a NAS or media server that speaks SMB, Hetzner supports Samba access. The SMB endpoint is `\\uXXXXXX.your-storagebox.de\backup` using the same credentials as SFTP, but password-based since Samba cannot use SSH keys.

On Linux, mount with cifs-utils. Samba access uses port 445 over Hetzner's network. From external networks (outside Hetzner), Samba is blocked at the firewall - you would need to use the Hetzner vSwitch or access from within the same Hetzner location.

Store credentials in a protected file rather than embedding them in fstab:

apt install cifs-utils -y

# Create credentials file
cat > /root/.storagebox_smb_creds << EOF
username=uXXXXXX
password=YOUR_PASSWORD
domain=WORKGROUP
EOF
chmod 600 /root/.storagebox_smb_creds

mkdir -p /mnt/storagebox_smb

# Mount command
mount -t cifs //uXXXXXX.your-storagebox.de/backup /mnt/storagebox_smb \
  -o credentials=/root/.storagebox_smb_creds,iocharset=utf8,file_mode=0660,dir_mode=0770

# fstab entry
//uXXXXXX.your-storagebox.de/backup /mnt/storagebox_smb cifs \
  credentials=/root/.storagebox_smb_creds,iocharset=utf8,_netdev 0 0

Borg Backup: The Primary Recommended Protocol

Borg Backup (borgbackup) is the protocol we recommend for most sysadmins using Hetzner Storage Box. Hetzner explicitly supports it, the Storage Box SSH environment has borg installed server-side, and it gives you deduplication, compression, and encryption without a separate backup server.

Install borgbackup on your source server. Debian/Ubuntu ship a reasonably current version, but check the Hetzner docs for the supported borg version on the Storage Box side - mismatches cause protocol errors. As of mid-2026, Storage Box supports borgbackup 1.2.x.

Initialize a repository on the Storage Box. The `repokey` encryption mode stores the key on the repo, protected by your passphrase. Use `keyfile` mode if you want the key stored locally only.

Set a BORG_PASSPHRASE environment variable or use a passcommand pointing to a secrets manager. Never hardcode the passphrase in a script that might end up in version control.

apt install borgbackup -y

# Check version
borg --version
# borg 1.2.8

export BORG_RSH="ssh -i /root/.ssh/storagebox_ed25519 -p 23"
export BORG_PASSPHRASE="your-strong-passphrase-here"

# Initialize repository
borg init --encryption=repokey \
  uXXXXXX@uXXXXXX.your-storagebox.de:repo1

# First backup
borg create \
  --verbose \
  --filter AME \
  --list \
  --stats \
  --show-rc \
  --compression lz4 \
  --exclude-caches \
  --exclude '/home/*/.cache/*' \
  --exclude '/var/tmp/*' \
  uXXXXXX@uXXXXXX.your-storagebox.de:repo1::'{hostname}-{now}' \
  /etc \
  /home \
  /var/www \
  /var/lib/mysql

# List archives
borg list uXXXXXX@uXXXXXX.your-storagebox.de:repo1

# Prune: keep 7 daily, 4 weekly, 6 monthly
borg prune \
  --list \
  --keep-daily 7 \
  --keep-weekly 4 \
  --keep-monthly 6 \
  uXXXXXX@uXXXXXX.your-storagebox.de:repo1

Automating Backups with Systemd Timers

Cron works, but systemd timers give you logging via journald, failure handling, and dependencies (the `OnCalendar` directive respects system timezone, and you can set `Persistent=true` to run missed jobs on next boot). We use systemd timers for all production backup automation.

Create a service unit and a timer unit. The service calls a backup script that handles the borg commands, checks return codes, and optionally sends a notification on failure. If you use a DevOps automation platform like taskbotshub.ai, you can push the backup status events there to trigger alerts or downstream workflows.

Reload systemd, enable, and check status:

# /etc/systemd/system/storagebox-backup.service
[Unit]
Description=Borg backup to Hetzner Storage Box
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
EnvironmentFile=/etc/storagebox-backup.env
ExecStart=/usr/local/bin/storagebox-backup.sh
IOSchedulingClass=best-effort
IOSchedulingPriority=7

---

# /etc/systemd/system/storagebox-backup.timer
[Unit]
Description=Run Borg backup daily at 02:30

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=600

[Install]
WantedBy=timers.target

---

# Enable and start
systemctl daemon-reload
systemctl enable storagebox-backup.timer
systemctl start storagebox-backup.timer
systemctl status storagebox-backup.timer
journalctl -u storagebox-backup.service -f
// advertisement

Sub-accounts for Multi-server or Multi-project Setups

One Storage Box can serve multiple servers or projects using sub-accounts. Each sub-account gets its own username (format: `uXXXXXX-subN`), password, optional SSH key, and can be locked to a specific subdirectory. This means your web server backup process cannot read or overwrite your database backup directory.

Create sub-accounts from the Hetzner Robot panel under Storage Box > Sub-accounts. You can set the home directory to `/backups/webserver` and another to `/backups/db-primary`. Add separate SSH keys per sub-account.

If you run multiple client environments or projects, this also keeps billing simple - the storage is pooled across sub-accounts against your box quota, so you see aggregate usage in one place.

For teams naming multiple projects consistently, tools like nicename.me can help establish a naming convention before you create the sub-account directory structure. Getting the hierarchy right before you create the first archive is much easier than renaming borg repos mid-production.

To list what is consuming space per subdirectory after a few months of backups:

# From within an SSHFS mount, check per-project usage
du -sh /mnt/storagebox/backups/*

# Or via SFTP non-interactively
echo "ls -la backups/" | sftp -i ~/.ssh/storagebox_ed25519 -P 23 \
  uXXXXXX@uXXXXXX.your-storagebox.de

# Check sub-account access (connects as sub-account)
sftp -i ~/.ssh/subaccount_ed25519 -P 23 \
  uXXXXXX-sub1@uXXXXXX.your-storagebox.de

rsync Use Case: Offsite Mirror for Local NAS

Not every use case is Borg. If you run a local NAS or a server where you want a straight mirror rather than a deduplicated archive, rsync directly over SSH is simpler and needs no special server-side tooling. This works well for static assets, media libraries, or software artifact repositories where you want file-for-file correspondence.

The key rsync flags for Storage Box: use `-e` to specify the SSH port, add `--partial` for resumable transfers, and use `--bwlimit` if you share the uplink with production traffic. On Hetzner infrastructure (your Cloud server and Storage Box in the same DC), skip `--bwlimit` - internal bandwidth is uncapped.

For large initial syncs (hundreds of GB), run rsync inside tmux or screen so an SSH drop does not kill the job. Add a cron or systemd timer for incremental runs afterward.

To track what changed in the last sync, redirect the itemized output to a log file:

# Full mirror with verbose itemized output
rsync -avz \
  --itemize-changes \
  --partial \
  --delete \
  --exclude='*.tmp' \
  --exclude='.DS_Store' \
  -e "ssh -i /root/.ssh/storagebox_ed25519 -p 23" \
  /srv/media/ \
  uXXXXXX@uXXXXXX.your-storagebox.de:/media-mirror/ \
  2>&1 | tee /var/log/storagebox-rsync-$(date +%Y%m%d).log

# Dry run to preview changes
rsync -avzn \
  --delete \
  -e "ssh -i /root/.ssh/storagebox_ed25519 -p 23" \
  /srv/media/ \
  uXXXXXX@uXXXXXX.your-storagebox.de:/media-mirror/

Database Backup Pattern: Dump, Compress, Transfer

Sending a live database directory to rsync is not safe. The correct pattern is: dump to a local temp file, compress, transfer, then delete the local temp. For PostgreSQL and MySQL this is well-established, but the transfer step to Storage Box deserves explicit handling.

For PostgreSQL, `pg_dumpall` or `pg_dump` per database. Pipe through gzip or zstd before writing to disk to reduce I/O. For MySQL, `mysqldump` with `--single-transaction` for InnoDB consistency.

This script runs a pg_dump and ships it to Storage Box, then prunes local temp files. Adapt the retention logic to your RPO requirements. We tested this with a 40GB PostgreSQL database: dump + compress + transfer to Storage Box (same Hetzner DC) completed in under 8 minutes.

#!/bin/bash
# /usr/local/bin/pg-storagebox-backup.sh

set -euo pipefail

BACKUP_DIR="/tmp/pg_backups"
REMOTE="uXXXXXX@uXXXXXX.your-storagebox.de"
REMOTE_PATH="/backups/postgres"
SFTP_KEY="/root/.ssh/storagebox_ed25519"
DATE=$(date +%Y-%m-%d_%H-%M)

mkdir -p "$BACKUP_DIR"

# Dump all databases
pg_dumpall -U postgres | zstd -9 > "${BACKUP_DIR}/pgdump_${DATE}.sql.zst"

# Transfer to Storage Box
rsync -az \
  -e "ssh -i ${SFTP_KEY} -p 23" \
  "${BACKUP_DIR}/" \
  "${REMOTE}:${REMOTE_PATH}/"

# Remove local temp files older than 2 days
find "$BACKUP_DIR" -name '*.zst' -mtime +2 -delete

echo "Backup completed: pgdump_${DATE}.sql.zst"
// advertisement

Snapshots and Point-in-Time Recovery

Hetzner Storage Box snapshots are server-side copies of your entire box at a point in time, taken from the Hetzner infrastructure layer. They are not the same as Borg archives - they do not deduplicate across time and they are not encrypted by your key. Snapshots protect against accidental deletion or a bad rsync run that deletes files you needed.

Create a snapshot from the Robot panel (Storage Box > Snapshots > Create Snapshot) or via the Hetzner Robot API. In 2026, the API endpoint is:

List existing snapshots:

Restoring a snapshot replaces your entire box contents with the snapshot state. There is no partial restore from the Hetzner UI. For partial file recovery, restore the snapshot to a different path by contacting Hetzner support, or rely on Borg archives for file-level recovery.

Our recommended setup: Borg for file-level and incremental recovery (your first line of defense), Hetzner snapshots as a safety net against catastrophic errors at the storage layer.

# Robot API: create snapshot (requires API credentials)
curl -u 'robotuser:robotpass' \
  -X POST \
  https://robot-ws.your-server.com/storage-box/uXXXXXX/snapshot

# List snapshots
curl -u 'robotuser:robotpass' \
  https://robot-ws.your-server.com/storage-box/uXXXXXX/snapshot

# Delete a snapshot by name
curl -u 'robotuser:robotpass' \
  -X DELETE \
  https://robot-ws.your-server.com/storage-box/uXXXXXX/snapshot/snapshot-YYYYMMDD

Storage Box vs S3-Compatible Object Storage

The common alternative to Storage Box is S3-compatible object storage - Hetzner Object Storage (S3-compatible, launched 2023), Backblaze B2, or AWS S3 itself. The right choice depends on your access pattern and tooling.

Storage Box wins when: you need SFTP or rsync protocol (many backup tools do), you want predictable flat pricing without per-request fees, you are already on Hetzner hardware and want internal-network throughput, or you run Borg (which maps naturally to SSH-based remote repos).

S3-compatible storage wins when: you need bucket-level ACLs and presigned URLs, you are building application-level storage (uploads, static assets via CDN), you need multi-region replication, or your tooling ecosystem is already S3-native (Restic with S3 backend, for example).

Restic with Storage Box over SFTP is a valid Borg alternative. Restic 0.16+ has the sftp backend stable and supports chunked uploads with deduplication. Performance on our test server (Hetzner CX22, Storage Box in same DC) showed Borg slightly faster on first backup (LZ4 compression outperforms Restic's zstd at equivalent ratios for our workload), but Restic's restore commands are friendlier.

For pure cost: BX31 at €14.90/month gives 5TB of flat-rate storage. Hetzner Object Storage at €0.0119/GB-month would cost €59.50/month for 5TB. For bulk backup use cases, Storage Box is 4x cheaper.

Monitoring Storage Box Usage and Alerting

Hetzner does not send alerts when you approach your quota. Build your own check and wire it into whatever monitoring stack you run.

A simple check via SFTP that parses disk usage and exits with a warning if above a threshold. Pipe this into Nagios/Icinga, Prometheus via a textfile collector, or a simple cron that emails on failure.

For teams using DevOps automation pipelines, taskbotshub.ai supports webhook-triggered workflows - you can push storage utilization metrics as events and trigger playbooks when thresholds are crossed, without building a separate monitoring application.

Also monitor your Borg repository health with `borg check` weekly - this verifies archive consistency and chunk integrity, catching bit rot before you need the backup.

#!/bin/bash
# /usr/local/bin/check-storagebox-quota.sh
# Returns 0=ok, 1=warning (>80%), 2=critical (>90%)

STORAGEBOX="uXXXXXX@uXXXXXX.your-storagebox.de"
KEY="/root/.ssh/storagebox_ed25519"
WARN_PCT=80
CRIT_PCT=90

# Parse df output via SFTP
USAGE=$(sftp -i "$KEY" -P 23 -b - "$STORAGEBOX" <<< "df" 2>/dev/null | \
  awk '/\/home\/uXXXXXX/ {gsub("%","",$5); print $5}')

if [ -z "$USAGE" ]; then
  echo "UNKNOWN: Could not retrieve usage"
  exit 3
elif [ "$USAGE" -ge "$CRIT_PCT" ]; then
  echo "CRITICAL: Storage Box ${USAGE}% full"
  exit 2
elif [ "$USAGE" -ge "$WARN_PCT" ]; then
  echo "WARNING: Storage Box ${USAGE}% full"
  exit 1
else
  echo "OK: Storage Box ${USAGE}% used"
  exit 0
fi

# Weekly borg check (add to systemd timer or cron)
borg check \
  --progress \
  uXXXXXX@uXXXXXX.your-storagebox.de:repo1
// advertisement