Security architecture
Who's actually behind an action once they're past the network gate SSH sits at, and what they're allowed to do.
Credentials
Five separate places identity actually lives here, not one — and they don't nest cleanly: a control at one layer doesn't contain a compromise at another.
Infrastructure credentials
Three separate credentials, not one — and they don't share the same strength:
- Infomaniak account (
<prefix>-infomaniak-user-admin) — the master login, with a second factor, and the actual root of trust behind both: it can reset Horizon's own password directly, not just reach it. - OpenStack Horizon (
<prefix>-openstack-user) — a separate credential day to day, but not truly independent, since the Infomaniak account can reset it at will. TOTP now covers the gap a bare password left open — see Provider Setup for the procedure. - OpenTofu's own credential (
<prefix>-openstack-app-credential) — held in Proton Pass, a scoped application credential for API-level provisioning only, not a human login at all.
None of this is something this server's own configuration touches — it's the provider account's own security.
Object storage credentials
<prefix>-openstack-s3-access-key — shared by OpenTofu's state backend and Coolify's backup destination, with no way to scope it to just one bucket — Infomaniak's Object Storage ACLs are additive-only, no per-bucket restriction exists. Accepted tradeoff, not an oversight.
SSH credentials
Three kinds of key worth distinguishing — what each is actually authorized to do turns out to vary more than the key material itself suggests. See Securing the system → SSH Hardening for the protocol-level settings (AllowUsers, PermitRootLogin) that govern which of these can actually connect at all.
- Infrastructure key (
<prefix>-ssh-infrastructure-key) — used to set up a server, meant to go unused afterward. Full access asdebian(sudoneeded for root); also authorized againstroot, but a forced-command restriction blocks a shell there. Private key lives only in Proton Pass, never on a server. The only one of the keys with reach beyond a specific box. - Server keys (
<prefix>-ssh-server-<name>-key) — authorized directly againstroot, unrestricted, used exclusively by Coolify's own automated server-management connections. The only kind of key here that actually grants a real root shell today. A different key per server is a deliberate choice worth making, not something Coolify does automatically.<prefix>-ssh-server-main-key— this server, currently the only one.
- Integrations keys (
<prefix>-ssh-integration-<service>-key) — Coolify-managed, one per service integration rather than reusing a single key across all of them. Reach is limited to whatever's inside a container, nothing beyond — not authorized against this server directly, and less risky as a result.<prefix>-ssh-integration-forgejo-key— pulls from Forgejo over SSH.
authorized_keys files accumulate entries over time — migrations, app deployments — without anything auditing what's actually there or why. That's exactly how the Forgejo key ended up sitting in root's file unnoticed, doing far more than it was created for — and, just as easily, how a key everyone assumes is fully privileged (the infrastructure key, against root) turns out to be quietly boxed in instead.
Todo — move off static keys. Coolify's Teams boundary is UI policy, not cryptographic: a key shared across multiple servers grants that same reach to anyone who extracts it, regardless of which team they belong to in the dashboard. An SSH certificate authority — OpenBao's SSH secrets engine, for instance — would replace static keypairs with short-lived certificates issued per connection, closing that gap for every server except whichever one hosts the CA itself, which still needs its own durable, non-delegated way in — the same root-of-trust problem the Infomaniak account already is for everything above.
Root password
Not SSH, not provider access — local console recovery only, if the box ever drops into emergency mode (a failed mount, for instance). PermitRootLogin and PasswordAuthentication stay whatever they're configured as regardless; this doesn't touch network access at all.
- Root password (
<prefix>-server-<name>-root-password) — set once viasudo passwd root, saved in Proton Pass immediately rather than left only in memory. Without it, emergency mode is a genuine dead end:suloginhas no password to check against, and the only way back in is a bootloader-level workaround (interrupting GRUB, booting withinit=/bin/bash).
Maintenance service credentials
Four services, four separate credential stores running on top of the OS entirely — not one identity system, and they don't share risk evenly:
- Authentik (
<prefix>-authentik-user-admin) — the identity provider itself, with its own local admin account. The actual root of trust behind every service that delegates login to it — a compromise here cascades to Forgejo (and anything else wired up to SSO later), though Coolify and Beszel's own local accounts wouldn't be touched by it. - Coolify — its own local admin account, created at first install, 2FA enabled. Reaches further than the others: admin access here can read every other service's environment variables, trigger any deployment, and reconfigure the server connection's own SSH settings — reaching into SSH credentials, not just sitting beside them. Someone with Coolify admin access could add a new SSH key for server management without ever touching
/root/.ssh/authorized_keysby hand.<prefix>-coolify-user-root-team— the admin login itself.<prefix>-coolify-env— the whole/data/coolify/source/.env, generated at first install:APP_KEY(encrypts everything Coolify stores in its own database, every managed service's environment variables included — without it, a database backup is encrypted data with no way to decrypt it, not a usable restore), plusDB_PASSWORD,REDIS_PASSWORD, and Pusher keys. See Machine Prep for the backup step this comes from.
- Forgejo and Beszel — both delegated to Authentik SSO rather than a local account each. Forgejo's password login can be disabled entirely once SSO is confirmed working, leaving Authentik as the sole gate. See Forgejo → Install → Authentik SSO.