Connections (Bring-Your-Own-Keys)
Wire HostSSH to your own storage and platform accounts — BYO buckets, choose how your keys are held, and unlock DNS cutover, web-SSH, alerts, and metrics.
Connections (Bring-Your-Own-Keys)
A Connection is one credential you hand to HostSSH so it can talk to something you already own — an object-storage bucket, a DNS zone, a VPS provider, a Slack channel. Two things to know up front:
- You bring the keys. BYO-keys is the default posture. Your encrypted
.hsiimages land in your bucket; cutovers happen in your DNS zone; provisioning uses your provider account. Managed storage and managed provisioning exist as paid add-ons, but nothing is locked behind them. - One Connection is required, the rest are upgrades. A single storage backend is all you need to start capturing. Every other Connection — DNS, access, alerts, metrics, git — is additive: it unlocks a capability without being a prerequisite.
There are two classes of Connection: storage backends (where images are written) and platform integrations (everything else). This guide covers both, plus how your keys are held (custody), what each Connection unlocks, and how retention ties in.
No storage Connection means no capture. Add storage first; layer integrations as you need them.
Add a storage backend
Storage is BYO. Pick a provider, scope a credential to one bucket/prefix, and add it. Every image is encrypted before it leaves the host (see Security), so a leaked bucket is not leaked data — bucket-side encryption is redundant and only AWS S3 exposes it below.
# Cloudflare R2 (the default — zero-egress restores)
hostssh connections add storage \
--provider r2 \
--account-id <cf_account_id> \
--access-key-id <key> --secret-access-key <secret> \
--bucket my-hsi-images --prefix prod/
hostssh connections test <id> # live probe: writes, reads, deletes a test object
hostssh connections list # status of every Connection (masked hints only)
Supported backends
restic is the substrate underneath, so any restic-compatible target works. These are the first-class, validated ones. Scope every credential to a single bucket/prefix.
| Provider | --provider | What it needs | Least-privilege tip |
|---|---|---|---|
| Cloudflare R2 (default) | r2 | account_id, access_key_id, secret_access_key, bucket, prefix | R2 API token scoped Object R/W on one bucket; restores have zero egress fees |
| AWS S3 | s3 | region, access_key_id, secret_access_key, bucket, prefix, optional sse (AES256 | aws:kms | off) | IAM user limited to s3:*Object* on bucket/prefix* only |
| S3-compatible | s3_compatible | endpoint, region, access_key_id, secret_access_key, bucket, prefix, force_path_style | Covers DO Spaces (<region>.digitaloceanspaces.com), Vultr Object Storage (<region>.vultrobjects.com), IDrive e2, Storj, and more |
| MinIO | minio | endpoint, access_key, secret_key, bucket, force_path_style=true, secure (TLS) | Dedicated MinIO service account + bucket policy |
| Backblaze B2 | b2 | endpoint (s3.<region>.backblazeb2.com), access_key_id (keyID), secret_access_key (appKey), bucket, prefix | Application key restricted to one bucket |
| Wasabi | wasabi | endpoint (s3.<region>.wasabisys.com), access_key_id, secret_access_key, bucket, prefix | Sub-user scoped to one bucket |
| SFTP | sftp | host, port, user, ssh_key | password, path | Dedicated restic user, chrooted to path |
| Local filesystem | local | path | For cross-box copies on hardware you own |
DO Spaces and Vultr Object Storage use the
s3_compatiblerow with the endpoints above. All eight backends work at every tier — what's tiered is the number of targets you can run, not which providers (see Tiers).
Pick your storage region/provider to match your data-residency needs — keeping images in an EU bucket (or US, or on your own hardware) is just a choice of backend.
Choose how your keys are held — custody
Separate from the storage credential is the image encryption key — the secret that
actually decrypts a .hsi. You choose how HostSSH holds it. This is the single most
important decision you'll make, because whoever holds an image and its key owns a
full copy of your server.
| Custody shape | Where the key lives | We can read your data? | Recovery if you lose the key |
|---|---|---|---|
agent_local | Encrypted only on your server — the control plane stores existence + status + a masked hint, nothing decryptable | No | None — the key never left your box; treat it like an air-gapped secret |
zero_knowledge | A passphrase you hold; the control plane keeps ciphertext but no key reference | No, ever | None — lose the passphrase, lose the backups (we warn hard, like restic's RESTIC_PASSWORD) |
escrow | Wrapped by the control-plane KMS so we can assist | Only to perform a recovery you ask for | One-click assisted recovery even if you forget the passphrase |
How to read this:
agent_localis for air-gapped or maximum-isolation installs. The encrypted key blob lives on the Agent host and nowhere else; the control plane can't decrypt anything, and restores run on the Agent with locally-held key material.zero_knowledgeis maximum security with a managed control plane: we hold the encrypted image metadata but can never read your data. Restores still run on the Agent with your passphrase. The trade-off is real — there is no "forgot password."escrowtrades a little of that for convenience: the key is wrapped by our KMS, so the dashboard's one-click recovery console can restore for you. Opt-in, documented.
Whatever you choose, the rules below always hold:
- Write-only over the API. You can set or rotate a credential; you can never read one back. The dashboard and CLI show a masked last-4 hint only.
- Never logged. Credentials never appear in Agent or control-plane logs, and probe errors are scrubbed before they're stored or returned — a token fragment can't leak through an error message.
- Per-brand keys. Credentials are encrypted at rest with a key scoped to your org (and, for resellers, per brand), so sibling brands never share a secret.
See Security for the full custody contract and threat model.
Platform integrations — what each one unlocks
Integrations are optional and stack on top of storage. Each one advertises a capability that the scheduler and migration engine key off. Add only what you want; prefer the most restricted token a provider offers.
# Example: a Cloudflare DNS Connection scoped to just your cutover zones
hostssh connections add dns \
--provider cloudflare \
--api-token <Zone:DNS:Edit token> \
--zone-ids zone_abc,zone_def
| Integration | Providers | What it needs | Least-privilege tip |
|---|---|---|---|
| DNS | Cloudflare, Route53 | CF: api_token (Zone:DNS:Edit) + zone_ids[]. R53: access_key_id, secret_access_key, hosted_zone_ids[] | Scope the token to only the zones used for cutover |
| Provider (VPS) | Hostinger, Hetzner, DigitalOcean, Vultr, Railway, AWS EC2, generic SSH / bare metal | api_token (+ project_id/team_id where it applies); generic SSH: host, port, user, ssh_key. Optional spend guard: max_monthly_spend, max_instances | ⚠️ Billing-capable. Prefer project-scoped tokens; Hostinger is account-wide and can spend money — use a dedicated sub-account |
| Access | Cloudflare Tunnel, Tailscale | CF: tunnel_token. TS: auth_key | Tailscale: an ephemeral, tagged key; CF tunnel scoped to the dashboard / web-SSH only |
| Notification | Slack, Discord, SMTP, webhook, PagerDuty, Telegram | Slack/Discord: webhook_url. SMTP: host,port,user,password,from. webhook: url,secret. PD: routing_key. TG: bot_token,chat_id | Webhook URLs are themselves secrets — they're stored encrypted and never logged |
| Monitoring | healthchecks.io, Datadog, Grafana/Prometheus, BetterStack | HC: ping_url. DD: api_key. Grafana: remote_write_url,token. BS: token | Datadog: a scoped app key, not an org-wide API key |
| Git | GitHub, GitLab | pat (+ repo) | Fine-grained PAT, single repo, contents-write only |
Provider-native DNS on Hostinger/DO/Vultr is reached through that provider Connection — you don't add a separate DNS row for it. Dedicated DNS Connections are Cloudflare and Route53.
The capability matrix
Each Connection carries one or more capability tags. This is the practical answer to "what do I get for adding this?"
| Connection | Capability tag(s) | What it turns on |
|---|---|---|
| Storage backend | restic_backend | Required to capture at all. Off-site copy = disaster recovery; on R2, zero-egress restores |
| DNS | dns_cutover | One-click DNS flip when you migrate or relocate a server |
| VPS provider | full_vps, object_store, dns_cutover | Managed migration (Mode 3), provisioning, and ephemeral restore-drill targets |
| CF Tunnel | tunnel, web_ssh | Zero-port dashboards plus browser web-SSH with no exposed :22 |
| Tailscale | mesh_access | Reach the Agent and dashboard over an overlay network — no public ports |
| Notification | alerts | Backup success/failure alerts and scheduled restore-drill reports |
| Monitoring | metrics_export, heartbeat | Push backup metrics and heartbeats to your own observability stack |
| Git | config_as_code | Export your Coolify / infrastructure definitions to a repo |
Two capabilities worth calling out, since they're what most people add storage next for:
web_ssh+tunnel(Cloudflare Tunnel) give you a browser shell with ephemeral, short-TTL keys and no open SSH port. See Web-SSH & access.dns_cutover(a DNS Connection) is what makes the migrate/relocate flow flip your domain automatically instead of leaving you to update records by hand. See Backups & recovery.
Retention ties to your storage target
Retention — "how many images to keep before older ones are deleted" — is set per
server × storage target, so a Connection and a retention policy go hand in hand. The
house default is keep everything (keep_all); auto-deletion is always explicit
opt-in, and every prune is logged and previewable before it runs.
hostssh retention set --target <storage-id> --keep-last 30 # keep the 30 newest
hostssh retention preview --target <storage-id> # dry-run: what would be pruned
hostssh retention get --target <storage-id>
hostssh retention clear --target <storage-id> # back to keep_all
| Setting | Meaning |
|---|---|
keep_last N | Keep the N most-recent images (the plain "image count before deletion") |
keep_hourly/daily/weekly/monthly/yearly | Grandfather-father-son retention buckets |
keep_within | Keep everything newer than a duration |
min_keep | Hard floor — never prune below this (default ≥ 1) |
quota / min_free_space | Capacity-based pruning: oldest unpinned image first, never below min_keep |
| locked / pinned images | Legal-hold — never auto-deleted; removing a lock is explicit and audited |
dry_run_default | Preview deletions before applying (on by default) |
Emergency restore is never gated — not by retention, not by quota, not by license state, not by a disabled Connection. Any reachable storage credential is restore-eligible. We will not hold your data hostage. See Security.
What each tier can add
All eight storage backends work at every tier. Gating is on the count of targets and on which integrations you can attach.
| Connection | Free / Trial | Solo | Pro | Agency / MSP |
|---|---|---|---|---|
| One storage backend | ✅ | ✅ | ✅ | ✅ |
| Multiple storage targets | — | — | ✅ | ✅ |
| Notifications / Monitoring | — | ✅ | ✅ | ✅ |
| Access (CF Tunnel / Tailscale) | — | ✅ | ✅ | ✅ |
| Git (config-as-code) | — | ✅ | ✅ | ✅ |
| DNS automation + Provider / provisioning | — | — | ✅ | ✅ |
| Reseller / billing (Stripe Connect) | — | — | — | ✅ |
The single source of truth for tiers, seats, and feature gating is Licensing.
Phase note. The packaged plans are being finalized as a five-tier lineup — solo, team, business, msp, enterprise. They map onto the gating above (e.g. team/business track today's Pro, msp/enterprise track Agency/MSP); exact seat counts and pricing land with the published price sheet. Until then, the table above is authoritative for what each Connection unlocks.
SaaS: enter your own platform keys to unlock more
If you run HostSSH through the hosted control plane, entering your own platform keys is what upgrades a basic install into a full operations surface. Concretely:
- Add a DNS Connection and migrations gain one-click cutover instead of manual record edits.
- Add a Provider Connection and the dashboard can provision fresh VPS targets for managed migration and spin up throwaway restore-drill targets on demand.
- Add a Monitoring Connection and your backup metrics and heartbeats flow into your own Datadog / Grafana / healthchecks — alongside the built-in telemetry dashboard.
- Add a Notification Connection and backup outcomes and restore-drill reports route to your Slack, PagerDuty, or inbox.
Every key you add is encrypted at rest, write-only over the API, scoped to your org (or brand), and shown only as a masked hint — the same custody and redaction rules as your storage credentials.
Manage Connections anywhere
Connections are first-class on every surface — read and delete exist everywhere, nothing is dashboard-only.
hostssh connections add <class> … # storage | dns | provider | access | notification | monitoring | git
hostssh connections test <id> # live probe; sets status pending|valid|invalid|disabled
hostssh connections list # all Connections with masked hints + status
hostssh connections get <id>
hostssh connections rotate <id> # replace the secret in place
hostssh connections rm <id>
hostssh retention set|get|list|preview|clear --target <storage-id>
The lifecycle is the same on all surfaces: add → validate → store (encrypted) →
periodic health re-check → rotate → revoke/disable. Validation is a live probe —
storage writes/reads/deletes a test object; other integrations make a cheap
authenticated call — and a periodic re-check surfaces drift when an upstream key is
rotated or expires (it flips to invalid so you can rotate before a backup fails).
The same actions are available in the local admin panel, the SaaS Connections /
Integrations page (with per-server overrides), the REST API (mTLS + scoped keys), and
the hostssh-mcp MCP server.
See also
- Install on any VPS — get the Agent on a server first
- Backups & recovery — capture, restore, clone, relocate
- Web-SSH & access — browser shells over CF Tunnel / Tailscale
- Licensing — tiers, seats, and what each plan includes
- Connections spec · Security · Provisioning — the internal source-of-truth specs