Skip to content

feat: add Garage S3-compatible storage recipe#59

Draft
troglodyne-bot wants to merge 3 commits into
Troglodyne-Internet-Widgets:masterfrom
troglodyne-bot:koan/garage-recipe
Draft

feat: add Garage S3-compatible storage recipe#59
troglodyne-bot wants to merge 3 commits into
Troglodyne-Internet-Widgets:masterfrom
troglodyne-bot:koan/garage-recipe

Conversation

@troglodyne-bot

Copy link
Copy Markdown
Contributor

What

Adds Provisioner::Recipe::garage — a provisioning recipe for Garage, a lightweight self-hosted S3-compatible object-storage server.

Why

Resolves #57. Garage solves the internal file-sharing problem better than WebDAV: it exposes a standard S3 API that any client can use, and it's a single statically-linked binary with minimal ops overhead.

How

  • Binary install: downloads the statically-linked musl binary from GitHub releases (version configurable, defaults to v1.0.1). Idempotent — skipped if the installed version already matches.
  • Config: /etc/garage.toml (mode 0600, owned by garage user) — S3 region, ports, data/metadata dirs, and rpc_secret are all configurable via recipes.yaml.
  • Service: systemd unit file, enabled on install, started post-provisioning via queue_postrun_task.
  • Init script: garage_init.sh is also queued post-start. It waits for Garage to accept connections, applies a single-node cluster layout (zone + capacity), and creates any configured buckets. Fully idempotent — safe to re-run.

Example recipes.yaml

somedomain:
    garage:
        rpc_secret: "your-64-hex-char-secret"   # openssl rand -hex 32
        buckets:
            - uploads
            - backups

Testing

  • Perl syntax check passes on lib/Provisioner/Recipe/garage.pm
  • Template structure manually verified against existing recipe patterns (matrix, koan, nginxproxy)
  • template_files keys match the rendered filenames used in garage.tt

🤖 Generated with Claude Code

Comment thread lib/Provisioner/Recipe/garage.pm Outdated

=over 4

=item C<rpc_secret> (required) — 64-character hex string used as the shared

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be a manual and required step, we should just run openssl rand -hex 32 and store it in a file rpc_secret.txt in the domain dir like we do with the generated key.rsa used to access guests made by trog-provisioner. This way other recipes relying on garage can immediately read this and configure appropriately.

Comment thread lib/Provisioner/Recipe/garage.pm Outdated
=item C<rpc_secret> (required) — 64-character hex string used as the shared
RPC secret between cluster nodes. Generate with C<openssl rand -hex 32>.

=item C<version> (optional, default C<v1.0.1>) — Garage release tag to download.

@teodesian teodesian Jun 2, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default version ought not be hardcoded below, but fetch the latest released version of garage as the default.

Comment thread templates/files/garage.service.tt Outdated
ExecStart=/usr/local/bin/garage -c /etc/garage.toml server
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LimitNOFILE ought to be configurable in the recipe, but default to this.

Comment thread templates/files/garage.toml.tt
@teodesian

Copy link
Copy Markdown
Contributor

@troglodyne-bot rebase

@troglodyne-bot

Copy link
Copy Markdown
Contributor Author

Rebase with requested adjustments

Branch koan/garage-recipe was rebased onto master and review feedback was applied.

Changes applied

  • uiring rpc_secret in recipes.yaml. Added _rpc_secret() which reads from $output_dir/rpc_secret.txt if it exists, otherwise generates one via openssl rand -hex 32, writes and chmod 0600s the file, then returns the secret. Matches the key.rsa pattern used elsewhere in trog-provisioner.
  • version fetches latest release (garage.pm:93-104): Replaced hardcoded v1.0.1 default with _latest_garage_version(), which queries the GitHub releases API (/repos/deuxfleurs-org/garage/releases/latest) via HTTP::Tiny + JSON::PP (both core Perl modules). Falls back to v1.0.1 with a warning if the request fails.
  • LimitNOFILE configurable (garage.pm:139, garage.service.tt:13): Added nofile_limit to validate() defaulting to 65536. Template now renders LimitNOFILE=[% nofile_limit %].
  • liblmdb0 dependency (garage.pm:88): Added liblmdb0 to deps() return since garage.toml.tt hardcodes db_engine = "lmdb", which requires the lmdb system library.

Stats

5 files changed, 265 insertions(+)
Actions performed
  • Already-solved check: negative (confidence=high, reasoning=No commit on master addresses Garage S3-compatible storage — the closest is 'feat: add deluged seedb)
  • Rebased koan/garage-recipe onto origin/master
  • Applied review feedback
  • Pre-push CI check: no CI runs found
  • Force-pushed koan/garage-recipe to fork
  • CI check enqueued in ## CI (async)

CI status

CI will be checked asynchronously.


Automated by Kōan

troglodyne-bot added a commit to troglodyne-bot/provisioners that referenced this pull request Jun 5, 2026
$opts{metadata_dir} //= '/var/lib/garage/meta';
$opts{replication_factor} //= 1;
$opts{s3_region} //= 'garage';
$opts{api_port} //= 3900;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to make sure all the ports we are specifying here are opened up in the UFW recipe in the event this recipe is loaded, like many other recipes do via conditional UFW app configs.

@teodesian

Copy link
Copy Markdown
Contributor

@troglodyne-bot rebase

troglodyne-bot and others added 2 commits June 5, 2026 20:06
Closes Troglodyne-Internet-Widgets#57. Adds Provisioner::Recipe::garage with a statically-linked
binary download, /etc/garage.toml config, systemd service, and an
idempotent post-start init script that applies a single-node cluster
layout and creates any configured buckets.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@troglodyne-bot

Copy link
Copy Markdown
Contributor Author

Rebase with requested adjustments

Branch koan/garage-recipe was rebased onto master and review feedback was applied.

Changes applied


  • Changes applied:
  • Added templates/files/ufw.garage.tt — UFW application profile registering Garage's four default ports (3900 S3 API, 3901 RPC, 3902 web, 3903 admin) as named app entries under /etc/ufw/applications.d/. Matches the hardcoded-defaults pattern used by ufw.plexmediaserver.tt and ufw.pdns.tt.
  • Registered ufw.garage.tt in lib/Provisioner/Recipe/ufw.pm %template2rule — adds 'ufw.garage.tt' => 'ufw/garage' so the UFW recipe conditionally includes the Garage firewall rules whenever the garage recipe is loaded. Addresses @teodesian's review comment requesting ports be opened via conditional UFW app configs.

Stats

7 files changed, 285 insertions(+)
Actions performed
  • Already-solved check: negative (confidence=high, reasoning=No commit on master references Garage, S3-compatible storage, or object storage — the feature is ent)
  • Rebased koan/garage-recipe onto origin/master
  • Applied review feedback
  • Pre-push CI check: no CI runs found
  • Force-pushed koan/garage-recipe to fork
  • CI check enqueued in ## CI (async)

CI status

CI will be checked asynchronously.


Automated by Kōan

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.

Garage target

2 participants