Automated backups for Discourse using hetzner object storage (S3 compatible)

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.


:light_bulb: How This Works

Discourse has built-in S3 backup support. When configured, it:

  1. Creates the backup file temporarily on local disk
  2. Uploads it to your S3 bucket
  3. 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.

:warning: 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.


:bucket: Step 1: Create a Bucket in Hetzner Cloud Console

  1. Log into console.hetzner.cloud
  2. Go to Object Storage in the left sidebar
  3. Click Create Bucket
  4. Choose a region — pick the same region as your multisite server (e.g. hel1 Helsinki, nbg1 Nuremberg, fsn1 Falkenstein)
  5. Give it a name, e.g. discourse-multisite-backups
  6. Leave Object Lock as Disabled (default) — you want to be able to delete old backups freely
  7. Leave Visibility as Private (default) — backups contain sensitive data and must not be public
  8. Click Create

:light_bulb: 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.yml as the folder name.


:key: Step 2: Generate S3 Access Credentials

  1. In the Object Storage section, go to Access Keys
  2. Click Generate Access Keys
  3. Save both the Access Key and Secret Key immediately — the secret is only shown once!
  4. Note your endpoint URL — it will look like https://hel1.your-objectstorage.com (with your region in place of hel1)

:gear: 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_REGION to your actual Hetzner region — this must match the region where you created your bucket
  • DISCOURSE_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_BUCKET or DISCOURSE_S3_CDN_URL — those are for storing uploads on S3, which is a separate project

Save and exit (Ctrl+X, Y, Enter).


:wrench: Step 4: Rebuild the App Container

cd /var/discourse && ./launcher rebuild app

Expect 5-15 minutes downtime while the container rebuilds and migrations run.


:desktop_computer: 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 frequency1 (daily)
  • maximum backups7 (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

:test_tube: 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.


:broom: 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 /

:money_bag: 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. :tada:


:shield: 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 backups setting controls retention in S3 — Discourse automatically deletes older backups beyond that number

(created with AI)

Keep an eye on this, or use backblaze B2 like I did, backups tend to fail silently on hetzner object storage. I realised this in a particularly tricky situation where I had a site malfunction and I needed a backup to only be surprised that my most recent was from 3-4 days ago despite me taking a full backup right before I began the maintenance. My only saving grace was that I also took a snapshot right before the maintenance so It kinda saved the day for me.

1 Like

Well that’s not good! I did read on meta that someone found hetzner object storage did not work well for serving up images.

What is backblaze b2? Ah. A similarly priced competitor. Looks promising and I will look into it!

How frequently do you typically create backups? The default is every 7 days. I think that is fine and then before doing something potentially destructive you create a backup first, and verify that it was created!

I will leave my backups on hetzner for now and see how it goes. It’s cheap and on the same console which makes it a bit easier to manage.

How frequently do you typically create backups? The default is every 7 days. I think that is fine and then before doing something potentially destructive you create a backup first, and verify that it was created!

I will leave my backups on hetzner for now and see how it goes. It’s cheap and on the same console which makes it a bit easier to manage

I usually have daily backups enabled for most sites

1 Like

And then save 5 bckups only going back 5 days?

IIRC backblaze had a generous free tier when I set it up, so I had bumped up the retained backups to 7 days.

1 Like