Skip to content

feat(funkwhale): optional nightly pg_dump → S3-compatible storage#32

Open
0cwa wants to merge 3 commits into
bonfirelink:masterfrom
0cwa:feat/funkwhale-backup
Open

feat(funkwhale): optional nightly pg_dump → S3-compatible storage#32
0cwa wants to merge 3 commits into
bonfirelink:masterfrom
0cwa:feat/funkwhale-backup

Conversation

@0cwa

@0cwa 0cwa commented Apr 29, 2026

Copy link
Copy Markdown

Adds opt-in nightly pg_dump → S3-compatible storage for the Funkwhale database. Default: off.

What

When funkwhale_backup_enabled: true, the role:

  • Installs awscli
  • Drops a backup script + 0600-mode env file with credentials
  • Installs a systemd service + timer (default: *-*-* 03:30:00 UTC, randomised by 15 min)
  • Extends tasks/assert.yml to require S3 credentials/bucket when the feature is on

How it streams

pg_dump | gzip | aws --endpoint-url … s3 cp - s3://…/funkwhale-<UTC>.sql.gz

The dump never touches the host disk in plaintext. After upload, the script lists the host's prefix and prunes objects older than funkwhale_backup_retention_days (default 30).

Defaults

Reuses the media bucket under backups/postgres/<host>/ so operators don't need a second Storj bucket. Override funkwhale_backup_bucket for isolation.

Restore command lives in roles/funkwhale/README.md.

Verified with

yamllint -c .yamllint.yaml .
ansible-lint --profile safety roles/funkwhale
ansible-playbook --syntax-check -i hosts/prod playbooks/funkwhale.yml
# + Jinja-rendered all four backup templates

Stacking

Depends on feat/funkwhale-core. After that merges, rebase this onto master.
Parallel with feat/funkwhale-anubis — they touch disjoint code paths.

0cwa added 3 commits April 29, 2026 22:31
…ugin

Adds roles/docker, a generic role that installs the upstream Docker
apt repository and the v2 'docker compose' plugin. No playbook
references it yet -- it is consumed by the upcoming funkwhale role
and is intentionally generic so any future containerised service can
reuse it.

Highlights:
- pulls Docker's official GPG key into /etc/apt/keyrings
- installs docker-ce + cli + containerd.io + buildx + compose plugin
- exposes docker_group_users so the funkwhale service user can run
  docker without sudo

Verified with: yamllint -c .yamllint.yaml .
Adds a Funkwhale role and playbook for music.bonfire.link.

Architecture:
  client → host nginx (TLS, big bodies)
            → front container (in compose stack)
              → api / celery / postgres / redis (in compose stack)
  audio  → S3-compatible object storage (Storj gateway by default),
           streamed directly to clients via signed URLs

Highlights:
  - public + federated by default; e-mail verification on
  - 5G upload ceiling (4h FLAC/WAV sets); proxy_request_buffering off
  - direct streaming from Storj (PROXY_MEDIA=false +
    AWS_QUERYSTRING_AUTH=true); the bucket can stay private
  - storage backend toggle (s3 | local) for Vagrant/dev
  - assert.yml refuses placeholder secrets; warns when registration
    is on without real SMTP
  - depends on roles/docker (added in feat/docker-role); reuses
    common, fail2ban, certbot and nginx roles untouched
  - public-repo-safe: no real secrets committed; vault layout
    documented in roles/funkwhale/README.md

Two add-ons land in follow-up PRs and are explicitly out of scope
here:
  - feat/funkwhale-anubis  — Anubis bot protection
  - feat/funkwhale-backup  — nightly pg_dump → S3

Verified with:
  yamllint -c .yamllint.yaml .
  ansible-playbook --syntax-check -i hosts/prod playbooks/funkwhale.yml
  jinja render of every template (compose YAML re-parses cleanly)
Adds an opt-in backup pipeline gated behind funkwhale_backup_enabled
(default false). When enabled, the role:

  - installs awscli
  - drops a backup script + 0600-mode env-file with credentials
  - installs a systemd service + timer (default 03:30 UTC nightly,
    randomised by 15 min)
  - extends assert.yml to require S3 credentials/bucket when the
    feature is on

The script streams 'pg_dump | gzip | aws s3 cp -' so the dump never
touches the host disk in plaintext. After the upload it lists the
host's prefix and deletes objects older than
funkwhale_backup_retention_days (default 30).

Defaults reuse the media bucket under backups/postgres/<host>/, so
operators don't need a second Storj bucket; override
funkwhale_backup_bucket if they want isolation.

Verified with:
  yamllint -c .yamllint.yaml .
  ansible-lint --profile safety roles/funkwhale  (passes)
  ansible-playbook --syntax-check -i hosts/prod playbooks/funkwhale.yml
  jinja-render of all four backup templates
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant