Outcome: Configure your Discourse sites to automatically send backups to Hetzner Object Storage (S3-compatible), so that backups are stored safely offsite and no longer accumulate on the server’s local disk.
When to use: Initial setup of offsite backups, or after a disk space crisis caused by local backup accumulation.
Prerequisites: A running Discourse server, and access to the Hetzner Cloud Console.
How This Works
Discourse has built-in S3 backup support. When configured, it:
- Creates the backup file temporarily on local disk
- Uploads it to your S3 bucket
- Deletes the local copy automatically
This means local disk usage stays minimal, and all your backups live safely in cheap offsite object storage.
For a multisite, S3 credentials are set once in app.yml as environment variables and apply to all sites. Each site then just needs its bucket name set in the admin UI.
Important: The backup process still needs a few GB of free local disk space to create the temporary file before uploading. If your disk is critically full, free up space first — see Fixing Disk Space Issues on a Discourse Multisite Server.
on a multisite, schedule backups at different times.
Step 1: Create a Bucket in Hetzner Cloud Console
- Log into console.hetzner.cloud
- Go to Object Storage in the left sidebar
- Click Create Bucket
- Choose a region — pick the same region as your multisite server (e.g.
hel1Helsinki,nbg1Nuremberg,fsn1Falkenstein) - Give it a name, e.g.
discourse-multisite-backups - Leave Object Lock as Disabled (default) — you want to be able to delete old backups freely
- Leave Visibility as Private (default) — backups contain sensitive data and must not be public
- Click Create
You only need one bucket for all your sites. Discourse automatically creates a subfolder path per site inside the bucket, using the Rails ID from
multisite.ymlas the folder name.
Step 2: Generate S3 Access Credentials
- In the Object Storage section, go to Access Keys
- Click Generate Access Keys
- Save both the Access Key and Secret Key immediately — the secret is only shown once!
- Note your endpoint URL — it will look like
https://hel1.your-objectstorage.com(with your region in place ofhel1)
Step 3: Add S3 Settings to app.yml
SSH into your multisite server and edit app.yml:
nano /var/discourse/containers/app.yml
Add the following to the env: section:
## Offsite backups to Hetzner Object Storage
DISCOURSE_USE_S3: true
DISCOURSE_S3_REGION: hel1
DISCOURSE_S3_ENDPOINT: https://hel1.your-objectstorage.com
DISCOURSE_S3_ACCESS_KEY_ID: "your-access-key-here"
DISCOURSE_S3_SECRET_ACCESS_KEY: "your-secret-key-here"
DISCOURSE_S3_INSTALL_CORS_RULE: false
DISCOURSE_BACKUP_LOCATION: s3
Notes:
- Set
DISCOURSE_S3_REGIONto your actual Hetzner region — this must match the region where you created your bucketDISCOURSE_S3_INSTALL_CORS_RULE: false— only needed if you use S3 for uploads too. For backups-only, set it to false.- Do not set
DISCOURSE_S3_BUCKETorDISCOURSE_S3_CDN_URL— those are for storing uploads on S3, which is a separate project
Save and exit (Ctrl+X, Y, Enter).
Step 4: Rebuild the App Container
cd /var/discourse && ./launcher rebuild app
Expect 5-15 minutes downtime while the container rebuilds and migrations run.
Step 5: Set the Backup Bucket in Each Site’s Admin UI
After the rebuild, visit each site’s admin panel. Go to Admin → Settings and search for s3 backup bucket. Set the value to your bucket name — the same for all sites:
discourse-multisite-backups
For multisite, Discourse will automatically append the Rails ID from multisite.yml and further subfolders to create the full path inside the bucket. You don’t need to predict or specify this path.
While you’re in backup settings, also configure:
backup frequency→1(daily)maximum backups→7(stored in S3 so disk space is not a concern)backup time→ stagger across sites so they don’t all run simultaneously, e.g.02:00,03:00,04:00,05:00,06:00
Step 6: Test Each Site
On each site go to Admin → Backups and click Backup Now. Watch the log — a successful S3 backup will show the file being uploaded then the local copy deleted. Then verify in the Hetzner Cloud Console → Object Storage → your bucket that files are appearing.
Step 7: Clean Up Existing Local Backups
Now that S3 backups are working, existing local backups are redundant. Free up that disk space:
# Check what's there first
du -sh /var/discourse/shared/web-only/backups/*/
# Remove local backups per site (adjust site names to match your multisite.yml Rails IDs)
rm -f /var/discourse/shared/web-only/backups/RAILSID/*.tar.gz
# Verify space recovered
df -h /
Cost Estimate
Hetzner Object Storage costs ~€0.023/GB/month. For low-volume Discourse sites with 7 daily backups per site:
5 sites × 7 backups × ~1GB per backup × €0.023 = ~€0.80/month
Essentially free, even as your sites grow. ![]()
Ongoing Maintenance
- Check the Hetzner console monthly to confirm backups are landing and bucket size is as expected
- Check local disk stays healthy:
df -h / - Test a restore occasionally — a backup you’ve never restored from is an untested backup!
- The
maximum backupssetting controls retention in S3 — Discourse automatically deletes older backups beyond that number
(created with AI)