Skip to main content

AlmaForge Standalone Deployment

AlmaForge is a self-hosted infrastructure access platform. It issues short-lived certificates for servers, Kubernetes clusters, databases, applications, and APIs, with protocol audit events and terminal replay under your recording policy. The control plane runs inside your environment, with no SaaS dependency or telemetry.

Looking for the 10-minute eval?

Try AlmaForge with the Quick Start demo.

Allow about 30 minutes once the host, DNS, and identity-provider prerequisites are ready. You will install AlmaForge on one Linux host, connect your identity provider, sign in, and open a server session. This setup is for evaluation and lab work. For production, start with Cluster on Linux or Cluster on Kubernetes.

For the control-plane and data-plane architecture, read How it Works.

What you need​

  • A Linux host. RHEL, AlmaLinux, Rocky Linux, or Oracle Linux 8+, Ubuntu 22.04 LTS+, or Debian 12+, on amd64 or arm64, with 2 vCPU, 2 GB RAM, 10 GB of free disk, and sudo access.
  • A DNS name that resolves to the host. An A or AAAA record is enough for this guide. Add a wildcard * record under the same name if you plan to publish internal web applications later.
  • Inbound TCP/443, reachable from your workstation and Let's Encrypt's public validation servers. This port carries the web UI, the CLI, and every protocol AlmaForge proxies.
  • Outbound HTTPS from the host to your identity provider and Let's Encrypt. See egress requirements.
  • An SSO identity provider that speaks OIDC: Okta, Microsoft Entra, Google Workspace, Keycloak, or another OIDC provider. Routine user sign-in uses SSO. Break-glass provides emergency administrator access for setup and recovery.

Create an OIDC application in that provider before you reach Step 5. It gives you the three values the connector needs: an issuer URL, a client ID, and a client secret. The SSO Integration guides walk through it per provider. You do not need it to start, because a new cluster has no sign-in method at all and break-glass is how you add one.

Creating that application takes administrator rights in the identity provider, and on Okta it also needs a custom authorization server. If someone else runs your identity provider, send them the relevant SSO Integration guide early. Waiting on that is usually the longest part of this page.

Every command below uses almaforge.example.com as the cluster hostname. Substitute your own DNS name throughout.

See System Requirements and Networking.

Step 1. Install the package​

Pick your distribution:

These instructions apply to APT-based (Ubuntu and Debian) distributions and their derivatives.

Supported APT-based Linux distributions

The APT package supports these distribution families. Check their upstream maintenance windows when choosing a production host:

DistributionCodenameVersionRelease DateUpstream EOL
UbuntuJammy22.04 LTSApril 2022April 2027
UbuntuNoble24.04 LTSApril 2024April 2029
DebianBookworm12June 2023July 2026 (regular), June 2028 (LTS)
DebianTrixie13August 2025August 2028 (regular), June 2030 (LTS)

See Debian release support for the distinction between regular support and LTS.

Other distributions with glibc 2.28+ may also work. Please contact us if you run into any issues.

The snippet below configures the https://get.almaforge.com repository and installs the almaforge package from the repository:

Terminal
sudo install -m 0755 -d /etc/apt/keyringscurl -sfLS https://get.almaforge.com/almaforge.gpg | \    gpg --dearmor | sudo tee "/etc/apt/keyrings/almaforge.gpg" >/dev/nullecho "deb [signed-by=/etc/apt/keyrings/almaforge.gpg] https://get.almaforge.com generic main" | \    sudo tee "/etc/apt/sources.list.d/almaforge.list"sudo apt-get updatesudo apt-get install -y almaforge
Required tools

curl, gpg, and sudo are often missing in stripped-down images.

Verify that almad is installed:

Terminal
almad versionAlmaForge v2026.1.0+gaef6f26d4c
Downloadable artifacts

The package installs the almad daemon, the alma CLI, the almad systemd unit, and /var/lib/almaforge for runtime state. The daemon runs as root and does not start during installation.

Step 2. Configure the daemon​

Configure almad with environment variables and CLI flags. The systemd unit reads environment variables from /etc/almaforge/almad.env at startup.

A package installation creates /etc/almaforge, /etc/almaforge/almad.d and /var/lib/almaforge with mode 0700. A direct tarball installation creates none of them, though the daemon creates /var/lib/almaforge itself on first start. These paths can contain credentials, and the root daemon reads them at mode 0700. After a tarball installation, create the two config directories:

Terminal
sudo install -d -o root -g root -m 0700 /etc/almaforge /etc/almaforge/almad.d

Save almad.env as /etc/almaforge/almad.env. Replace almaforge.example.com with your DNS name and use an email address you control:

examples/quickstart/almad.env
# Environment file for a single-host AlmaForge cluster.
#
# One almad process runs the Auth, Proxy, and SSH Services. The host is
# both the control plane and your first server. Split those roles across
# separate hosts before production.
#
# Path: /etc/almaforge/almad.env
# Permissions: chown root:root, chmod 0600

ALMA_AUTH_SERVICE=true
ALMA_PROXY_SERVICE=true

# Serve this host as an SSH target without provisioning a second
# machine. Remove this setting when the control plane should not accept
# user sessions.
ALMA_SSH_SERVICE=true

# Multiplex the web UI, CLI, Kubernetes, databases, and agent reverse
# tunnels on one TLS port through SNI and ALPN. The packaged systemd
# unit runs as root and can bind port 443.
ALMA_LISTEN=0.0.0.0:443

# Public hostname for clients and agents. It must match the DNS record
# and TLS certificate. It also forms the base of SSO redirect URLs and
# the ACME challenge below.
ALMA_ADVERTISE=almaforge.example.com:443

# Let's Encrypt issues and renews the certificate over the TLS-ALPN-01
# challenge on port 443. Port 443 must be reachable from Let's
# Encrypt's public validation servers. Swap for ALMA_TLS_CERT and
# ALMA_TLS_KEY to use a certificate from your own CA.
ALMA_ACME_ENABLED=true
[email protected]

# ALMA_LABELS sets operator-defined labels. Use stable keys such as env,
# region, and team. Keep fast-changing values out of this setting.
#
# The agent also advertises generated labels. It refreshes AWS and Azure
# tags from instance metadata once an hour and prefixes their keys
# with aws/ or azure/. Hetzner supplies hetzner/instance-id,
# hetzner/region, hetzner/availability-zone, hetzner/hostname, and
# hetzner/local-ipv4. Every host that sends heartbeats also advertises
# internal/expires. Role serverLabels matchers evaluate the full set.
ALMA_LABELS=env=demo

# Without a storage URI, cluster state, audit events, and session
# recordings stay on local disk under this directory. Use this default
# for evaluation only.
ALMA_DATA_DIR=/var/lib/almaforge

# Resource manifests placed here are applied at every start.
ALMA_CONFIG_DIR=/etc/almaforge/almad.d

Cluster state, audit events, and session recordings default to local disk under /var/lib/almaforge, so this file names no storage backend. Move them to PostgreSQL, etcd, S3, or another backend before production. See the Storage Guide.

The file is read by root only:

Terminal
sudo chown root:root /etc/almaforge/almad.envsudo chmod 0600 /etc/almaforge/almad.env

This setup uses built-in Let's Encrypt certificate management. For a private endpoint or an organization-managed certificate, see operator-supplied TLS certificates.

Every supported variable is listed in Environment Variables.

Step 3. Start the daemon​

Terminal
sudo systemctl enable --now almadsudo systemctl is-active almadactive

The first start initializes cluster state, creates the cluster certificate authority, and requests a TLS certificate over ACME. Watch startup and certificate issuance in the logs:

Terminal
sudo journalctl -u almad -f

If the daemon does not reach active or the page below does not load, check DNS and port 443 first. ALMA_ADVERTISE must match a name that resolves publicly to this host. Let's Encrypt must also reach port 443 to issue the certificate. See Troubleshooting.

Open https://almaforge.example.com/ in a browser. Because no identity provider is connected, the sign-in page shows Set up this cluster. The card prints the next command with this host's addresses already filled in.

Step 4. Sign in with break-glass​

The cluster cannot accept an SSO sign-in until you add a connector. Break-glass turns shell access on the cluster host into one short browser session so you can add it.

Run the command on the almad host. /var/lib/almaforge is owned by root, so use sudo:

Terminal
sudo alma login --breakglass
Break-glass recovery requested. Open this link within five minutes:
https://almaforge.example.com/login#breakglass=<claim>
It can be used once and opens a short administrator session as "breakglass".

If you changed the data directory or the port almad listens on, add --data-dir=<path> and --auth-server=<host:port>. The sign-in page prints the command with this host's values already filled in. Copy it from there.

Open the link yourself in a browser within five minutes. Do not forward it. The one-time claim sits in the URL fragment and stays out of server and proxy logs. Whoever opens the link first gets the session. Confirm on the Emergency administrator access card to enter the web UI.

The built-in breakglass user carries the wildcard admin role. Its sessions last at most one hour and cannot be renewed. Run the command again if you need a fresh session.

note

Filesystem access to /var/lib/almaforge on an Auth host authorizes break-glass. Anyone who can read that directory can use it. Treat every use as a security event and restrict access to the host. The full procedure, including day-2 recovery, is in Break-Glass Access.

Step 5. Connect your identity provider​

Your emergency session lasts one hour. Use it to connect your identity provider.

In the OIDC application you created for What you need, register this callback URL. It is your cluster hostname plus a fixed path: https://almaforge.example.com/api/v1/oidc/callback.

Then create the connector. Both paths produce the same resource.

In the sidebar, open Access control, then SSO, and add a connector. Choose your provider to load a starter template. Enter the issuer URL, client ID, client secret, and claim mapping.

claimsToRoles decides who gets in and what they can do. Each sign-in evaluates every entry and grants the union of all matching roles. For this evaluation, ensure a claim you hold maps to admin, which lets you configure the cluster and connect to its resources. The example grants admin and access to the platform-admins group. Replace that group with a value from your provider.

A new cluster includes these roles for different responsibilities:

RoleGrants
memberSee every resource and request access to it. Nothing else.
accessConnect to resources the role's label matchers select, including as root, ec2-user, or ubuntu on every server.
auditorRead-only, including the audit log and recordings.
adminEverything, with a one-hour session cap.

reviewer decides the requests a member files. See Just-in-time access. When the defaults no longer fit, use the RBAC Guide to write your own roles.

Map a claim your provider returns for you, such as your email address or a group you belong to. For a group mapping, value has to match the group exactly as your provider emits it, which is not always the name you see in its admin console. The SSO Integration guides show where to read that value for each provider, along with the issuer URL.

If no mapping matches, sign-in is refused rather than granting an empty session. Run sudo alma login --breakglass on the cluster host again, correct the connector, and retry.

Success signal: after you sign out and reload https://almaforge.example.com/, the sign-in page shows your provider instead of the setup card.

Step 6. Install the CLI and sign in as yourself​

Install alma on your macOS or Linux workstation:

Terminal
curl -fL https://get.almaforge.com/install.sh | sh

The installer places both alma and almad on your workstation and creates /etc/almaforge/almad.d and /var/lib/almaforge. It sets /var/lib/almaforge to mode 0700. A Linux package installation also adds a disabled systemd unit. The workstation uses alma for sign-in. almad remains stopped.

Windows runs through WSL. See CLI installation for package manager and signed installer options.

Sign in against the cluster:

Terminal
alma login --proxy=almaforge.example.com:443

A browser opens for the SSO flow. The cluster then issues a short-lived certificate bound to the roles mapped from your claims. Check the identity:

Terminal
alma status

Open your first server session​

The configuration in Step 2 also enabled the SSH Service, so the cluster host is already your first server. List it, then connect using an OS account that exists on that host and is allowed by your role:

Terminal
alma get nodesalma ssh <login>@<host>

The built-in admin and access roles allow root, ec2-user, and ubuntu. Use the hostname returned by get nodes. See server access roles if your host uses a different login.

Success signal: the shell opens through AlmaForge. After you exit, open Recordings in the Web UI and replay the session. Your evaluation cluster is ready. The remaining sections explore it and add resources.

Look around​

Web UI​

The sidebar at https://almaforge.example.com/ covers daily operations:

SectionWhat is there
ResourcesEvery server, Kubernetes cluster, database, and application you can reach, with a connect dialog for each.
RequestsAsk for elevated roles you do not hold, and review other people's requests. See Just-in-time access.
SessionsSessions happening right now.
Audit LogEvery sign-in, connection, and configuration change.
RecordingsReplay of finished sessions.
SSOThe connector editor from Step 5.
RolesWhat each role can reach and what it can do. See the RBAC Guide.
UsersWho has signed in, and the roles they carry.
LocksBlock a user, role, or host once the lock reaches the enforcing services, typically within seconds over a healthy Auth connection. See Revoking access.
InstancesThe almad processes in the cluster and their versions.
StorageWhich backends hold state, audit events, and recordings.

CLI​

If you use kubectl, the model is familiar. Resources are apiVersion / kind / metadata / spec documents. The same verbs work across all of them. These are command examples for separate tasks, not a sequence to run. apply, edit, and delete change cluster resources:

Terminal
alma status                       # current identity, roles, expiryalma get nodes                    # reachable SSH hostsalma get role                     # all rolesalma get role/access -o yaml      # one resource in fullalma apply -f <role-file>.yaml    # create or updatealma edit role/<role-name>        # open in $EDITOR, apply on savealma delete role/<role-name>      # remove the named rolealma ssh <login>@<host>           # open a sessionalma kube login <name>            # write a kubeconfig entryalma db login <name>              # issue a database client certificatealma recording ls                 # list session recordingsalma logout                       # discard the local identity

Certificates expire automatically. Run alma login again when a new sign-in is required. The kubeconfig integration obtains credentials for kubectl through alma. See the CLI Reference for the full command tree.

Step 7. Add your resources​

The control plane, SSO, and first server session are working. The resource sections below are optional and independent. Use only the ones you need, or continue to production planning.

The agent examples below follow three steps: issue a join token, start an agent with that token, and grant access through RBAC. The agent maintains an outbound connection to the Proxy. Agent hosts need no inbound connection from the Proxy or public address. The access guides also cover other join methods and deployment options.

Servers​

The Step 2 env file set ALMA_SSH_SERVICE=true, so the cluster host is already an SSH target. Other machines join with a token. Issue one from your workstation, using the identity from Step 6:

Terminal
alma create token --services=sshToken created.  client_id:     bbf4a1c2d3e4f5a6b7c8d9e0f1a2b3c4  client_secret: 9f8e7d6c5b4a39281706f5e4d3c2b1a0Save the client_secret now, it cannot be retrieved later. The clusterstores only its SHA-256 digest.
This token will expire in 30 minutes.

The token can enroll multiple hosts before it expires. Each host trades it for a separate identity.

On the new machine, install the package as shown in Step 1, then write /etc/almaforge/almad.env:

/etc/almaforge/almad.env
ALMA_PROXY=almaforge.example.com:443
ALMA_CLIENT_ID=bbf4a1c2d3e4f5a6b7c8d9e0f1a2b3c4
ALMA_CLIENT_SECRET=9f8e7d6c5b4a39281706f5e4d3c2b1a0
ALMA_SSH_SERVICE=true
ALMA_LABELS=env=demo

ALMA_PROXY routes the join through the Proxy. The agent validates the Proxy's TLS certificate against the host's system trust store. Step 3 configured a Let's Encrypt certificate, which the public trust store already recognizes.

ALMA_CA_PIN pins the Auth Service CA for a direct join selected by ALMA_AUTH_SERVER. Configure one join address, not both ALMA_AUTH_SERVER and ALMA_PROXY. Proxy joins ignore ALMA_CA_PIN. For a private Proxy certificate, install the signing CA in the host's trust store. See cluster authentication for both trust paths.

Terminal
sudo chmod 0600 /etc/almaforge/almad.envsudo systemctl enable --now almad

The host appears within seconds:

Terminal
alma get nodesHost    UUID                                  Public Address  Labels    Version------- ------------------------------------- --------------- --------- -------web-01  3f0a6e2c-1d4b-4f3e-9c2a-5d7e8b9c0f1a                  env=demo  2026.1.0

Open a session with an OS login your role allows:

Terminal
alma ssh <login>@web-01

If the error says your role does not allow the requested OS login, add an account that exists on the host:

Terminal
alma edit role/access

Your editor opens on the role. Add the login under spec.allow.serverLogins, save, then run alma login again to obtain a certificate that permits the added login. Retry the SSH connection. See Server Access for secretless cloud join, agentless OpenSSH hosts, and host user creation.

Success signal: the shell opens. After you exit, the session appears under Recordings.

Kubernetes clusters​

The agent runs inside the target cluster. The Kubernetes API needs no public endpoint.

Point kubectl at the target cluster through your existing admin path: gcloud container clusters get-credentials, aws eks update-kubeconfig, or your own kubeconfig. The manifests below create a Namespace and a ClusterRole, so this step requires cluster-admin. Keep that path working for recovery if the agent enters a bad state.

Issue the join token, then apply the agent manifest served by the Proxy. The manifest is the same for every cluster:

Terminal
alma create token --services=kuberneteskubectl apply -f https://almaforge.example.com/install.yaml

The pods wait for two cluster-specific objects: the join credentials and a KubernetesTarget. Both belong in the almaforge namespace created by the manifest.

Terminal
kubectl -n almaforge create secret generic almaforge-env \    --from-literal=ALMA_PROXY=almaforge.example.com:443 \    --from-literal=ALMA_CLIENT_ID=<client-id-from-the-token> \    --from-literal=ALMA_CLIENT_SECRET=<client-secret-from-the-token> \    --from-literal=ALMA_KUBERNETES_SERVICE=true \    --from-literal=ALMA_MATCH_LABELS='*=*' \    --from-literal=ALMA_DIAG_LISTEN=0.0.0.0:3000

ALMA_MATCH_LABELS selects the targets this agent serves. Without it, the agent starts but watches nothing. ALMA_DIAG_LISTEN is also required because the StatefulSet probes /healthz and /readyz on port 3000.

kubernetes-target.yaml is the cluster's own registration:

examples/quickstart/kubernetes-target.yaml
# Registers the Kubernetes cluster where the agent runs. Add it to the
# almaforge-resources ConfigMap under the kubernetes.yaml key. The
# ConfigMap mounts at /etc/almaforge/almad.d, where almad reads resource
# files and hot-reloads changes.
apiVersion: almaforge.com/v1
kind: KubernetesTarget
metadata:
name: prod-eu-central # The name users type into `alma kube login`.
labels: # Matched by Role.kubernetesLabels and by ALMA_MATCH_LABELS.
env: prod
region: eu-central
# An empty spec makes the agent broker its local cluster with its
# mounted ServiceAccount token. To broker a remote cluster instead,
# supply a kubeconfig on the agent host.
spec: {}

Save it under that name in the current directory, then load it:

Terminal
kubectl -n almaforge create configmap almaforge-resources \    --from-file=kubernetes.yaml=kubernetes-target.yaml

The agent mounts the ConfigMap at /etc/almaforge/almad.d/, the same drop-in directory used on a VM. It reads the file once at startup. Kubernetes updates the mounted copy when you change the ConfigMap, but a running agent keeps the configuration it started with, so roll the StatefulSet after any edit.

The pods start after both objects exist. Wait for the rollout, then confirm that AlmaForge sees the cluster:

Terminal
kubectl -n almaforge rollout status statefulset/almaforge --timeout=120salma get kubeCluster          Labels                      Version---------------- --------------------------- -------prod-eu-central  env=prod,region=eu-central  2026.1.0

Point kubectl at it:

Terminal
alma kube login prod-eu-centralkubectl get pods -A

kube login writes a kubeconfig context backed by your short-lived certificate. Each call is checked against your roles and recorded in the audit log. Your role needs kubernetesGroups and kubernetesLabels to allow the request.

After the agent starts, revoke the join token with alma delete token/<client-id>. See Kubernetes Access to inspect or adapt the agent manifests, join through the cluster's ServiceAccount without a secret, or run the agent outside the cluster with a kubeconfig. To generate the commands in the web UI, open Resources, then Enroll resource.

Success signal: kubectl get pods -A returns, and the call appears in Audit Log.

Web applications​

Publish internal dashboards and APIs at <name>.almaforge.example.com. Users authenticate through the same SSO connector, with no VPN or inbound exposure of the upstream.

This requires the wildcard * DNS record from What you need. ACME issues a certificate when each app hostname is first requested. No per-app certificate setup is required.

Issue a token from your workstation:

Terminal
alma create token --services=app

On a host that can reach the app, install the package as shown in Step 1, then write /etc/almaforge/almad.env:

/etc/almaforge/almad.env
ALMA_PROXY=almaforge.example.com:443
ALMA_CLIENT_ID=<client-id-from-the-token>
ALMA_CLIENT_SECRET=<client-secret-from-the-token>
ALMA_APP_SERVICE=true
ALMA_SSH_SERVICE=false
ALMA_MATCH_LABELS=*=*
Terminal
sudo chmod 0600 /etc/almaforge/almad.envsudo systemctl enable --now almad

ALMA_MATCH_LABELS is what makes this agent pick up the app you register below. Without it the agent starts and serves nothing. Narrow it from *=* to something like env=prod to split apps across several agents.

Then register the app itself. Save http-apptarget.yaml to a file, replace spec.uri and spec.fqdn with your own values, and apply it with alma apply -f:

examples/applications/kinds/http-apptarget.yaml
# HTTP/HTTPS web app behind the App agent. Users open it at
# https://<name>.<cluster> in a browser.
apiVersion: almaforge.com/v1
kind: AppTarget
metadata:
name: grafana # DNS-label name; users type this and it becomes <name>.<cluster>.
labels: # Static labels matched by Role.appLabels for RBAC.
env: prod
team: sre
spec:
uri: http://grafana.monitoring.svc:3000 # Upstream URL; scheme picks http / https / tcp.
fqdn: grafana.almaforge.example.com # Externally visible hostname; must be covered by the Proxy's wildcard cert.

spec.uri is the upstream address, which only the agent host must reach. spec.fqdn is the public name users enter, and it has to sit under the wildcard DNS record for this cluster. Confirm the app is registered, then open it:

Terminal
alma get apps

Your role needs an appLabels matcher that selects the app. Otherwise, the Proxy returns 403. See Application Access for header injection, JWT identity propagation, TCP apps, and mTLS service-to-service access.

Success signal: after the SSO redirect, https://grafana.almaforge.example.com loads in the browser.

Databases​

PostgreSQL, MySQL, MongoDB, and other databases use --services=database and a DatabaseTarget. After running alma db login, users connect with their normal client and a credential issued by AlmaForge. See Database Access.

What's next​

This single-host cluster stores everything on local disk and runs one of each service. Before serving production traffic: