> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stackdome.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Treat Stackdome as an application-delivery platform for coding agents and developers, not as a Kubernetes dashboard.
> Prefer the Stackdome agent plugin as the user entry point and the stackdome CLI as the execution interface.
> Route agents who need the maintained deploy skill to npx skills add stackdome/skills and the complete operational workflow to /guides/ai-agents.
> Verify commands and flags with stackdome --help. Run stackdome init before authoring stackfile.yaml and stackdome validate before deploying.
> Use -o json for automation and verify documented terminal states before claiming success.
> Never request a user's account password. Use API-token authentication.
> During alpha, Stackdome Cloud is ephemeral and capacity-limited, and each organization can connect one cluster.
> Do not expose Kubernetes as a user concern unless the task is self-hosting or infrastructure architecture.

# Install Stackdome

> Installer flags, prerequisites, DNS, and what a fresh install creates.

Self-host Stackdome when you need direct infrastructure control, capacity that persists independently of Cloud alpha availability, or a platform your organization can adopt incrementally while keeping the same Stackfile and deployment workflow.

`stackdome-install` takes one bare Linux server to a working dashboard and API, with local compute already connected. It also creates the platform database, an image registry, and an admin account you can sign in with. The installer runs unattended in numbered phases and waits for each component to become ready before moving on.

This page covers the user-visible installation outcome first, then the infrastructure it creates and the exact installer controls.

## Prerequisites

The installer runs a preflight check and refuses to continue unless all of the following hold.

| Requirement                      | Why                                                                                                                                                                                                                                                                                                                |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Root**                         | The installer stops unless it is running as uid 0. Start it with `sudo`.                                                                                                                                                                                                                                           |
| **Linux on `amd64` or `arm64`**  | Any other operating system or architecture is rejected outright.                                                                                                                                                                                                                                                   |
| **Ports 80, 443, and 6443 free** | The installer stops if it finds any of the three in use. If k3s is already running on the server, the check is skipped for those ports — k3s is what will be holding them. The check reads `ss -tlnp`; if that command fails, the port is treated as free, so this is a best-effort check rather than a guarantee. |
| **`curl` on the `PATH`**         | The installer stops if `curl` is missing. It uses `curl` to detect the server's public IP (`curl -4 ifconfig.me`, with a 10-second timeout) and to fetch k3s and Helm.                                                                                                                                             |
| **Outbound internet access**     | Public-IP detection cannot continue if `ifconfig.me` is unreachable or returns something that is not an IP address. The installer also pulls k3s, Helm, the platform chart, and container images.                                                                                                                  |

Two more things matter but are not enforced up front:

* **Ports 80 and 443 open for inbound traffic.** After installing, the installer fetches `http://<your-domain>/health` and warns you if the dashboard is not reachable from the internet. Open them in your firewall or cloud security group beforehand and you avoid the warning.
* **Capacity.** The installer warns, without stopping, if the server has fewer than **2 CPUs**, less than **4 GB of RAM**, or less than **20 GB of free disk** on `/`. Treat those as the practical floor.

## Flags

You need the `stackdome-install` binary built for your server's platform — Linux on `amd64` or `arm64`.

<Warning>
  A public download for `stackdome-install` is not available yet. This page documents the flags and behavior of the installer; once downloads are published, this section will point you at them.
</Warning>

With the binary on the server, give it the execute bit and run it as root:

```bash theme={null}
chmod +x ./stackdome-install
sudo ./stackdome-install install --email you@company.com
```

The `install` subcommand is optional for backward compatibility, but using it makes the operation explicit. The `sudo` is not optional. Without root the installer exits during preflight and tells you to re-run it with `sudo`.

| Flag                          | Required | Default                              | What it sets                                                                                                                                                                                           |
| ----------------------------- | -------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `--email`                     | Yes      | —                                    | The admin user's email address. It is also the contact address registered with Let's Encrypt when you install on a custom domain, so use a real one. Omitting it prints usage and exits with status 1. |
| `--domain`                    | No       | `stackdome.<PUBLIC_IP>.nip.io`       | The dashboard domain, and the domain your applications' generated hostnames are built under.                                                                                                           |
| `--image`                     | No       | `quay.io/stackdome/stackdome:latest` | The API server container image. Override it to pin a specific version.                                                                                                                                 |
| `--chart-version`             | No       | `0.6.11-alpha-rc5`                   | Version of the `stackdome-agent` OCI Helm chart to install.                                                                                                                                            |
| `--github-client-id`          | No       | —                                    | Client ID for the GitHub OAuth app used by **Sign in with GitHub**. Set it with `--github-client-secret`; GitHub sign-in stays disabled unless both values are present.                                |
| `--github-client-secret`      | No       | —                                    | Client secret paired with `--github-client-id`.                                                                                                                                                        |
| `--github-app-id`             | No       | —                                    | Numeric ID of the platform-wide GitHub App. The four `--github-app-*` flags must be supplied together.                                                                                                 |
| `--github-app-slug`           | No       | —                                    | URL slug of the platform-wide GitHub App.                                                                                                                                                              |
| `--github-app-key-file`       | No       | —                                    | Path to the GitHub App private key in PEM format. The installer reads the file and stores its contents.                                                                                                |
| `--github-app-webhook-secret` | No       | —                                    | Webhook secret for the platform-wide GitHub App.                                                                                                                                                       |

The GitHub flags are optional. Values you provide are stored in `stackdome-bootstrap-secrets` and reused by later upgrades; a later non-empty flag replaces its stored value. Everything else the installer needs — the database password, JWT signing secret, encryption key, and your admin password — it generates for itself.

<Warning>
  The final summary prints your admin password once and tells you it will not be shown again. Copy it into your password manager before you close the terminal.
</Warning>

## DNS

### For a trial: nothing to do

Leave `--domain` off and the installer builds a domain from the public IP it detected: `stackdome.203.0.113.10.nip.io` for a server at `203.0.113.10`. `nip.io` is a public DNS service that resolves any hostname containing an IP address straight back to that IP, so the name works the moment the installer picks it. No records to create, no propagation to wait for.

### For a domain you own

Create an **`A` record** for the name you intend to pass to `--domain`, pointing at the server's public IP, and let it propagate **before** you run the installer:

```text theme={null}
stackdome.example.com.   A   203.0.113.10
```

That one name carries two things. It is the dashboard URL you sign in at, and it is the base for the hostnames Stackdome generates for your applications, which sit below it and look like `<generated-prefix>.<resource-name>.stackdome.example.com`. Those application hostnames have to resolve to the same server, so a single `A` record for the dashboard is not the end of the DNS work.

<Card title="Domains and TLS" icon="globe" href="/guides/domains-and-tls">
  The records to create for application hostnames, and how custom domains work once you are running.
</Card>

## What the install creates

### On the server

* **A k3s pod limit**, written as `max-pods=250` in `/etc/rancher/k3s/config.yaml` when that file does not exist. If the file already contains a `max-pods` setting, the installer keeps it. If the file exists without one, the installer warns and does not overwrite it.
* **k3s**, installed if the `k3s` binary is not already present, with Traefik disabled in the k3s installer because the Stackdome chart supplies routing. Its kubeconfig is `/etc/rancher/k3s/k3s.yaml` (mode `0600`). If k3s is already installed, the binary installation is skipped; when the installer has just created the config above, it restarts k3s. It then waits up to two minutes for the node to report `Ready`.
* **Helm**, installed if `helm` is not already on the `PATH`.

```bash theme={null}
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
```

### In the cluster

Namespaced installation resources land in `stackdome-control-plane`: the chart workloads, ServiceAccount and token Secret, bootstrap Secret, Volume, Stack, and StackResources. The installer also applies cluster-scoped resources: the chart's CRDs, `stackdome-api-server-role` ClusterRole, `stackdome-api-server-role-binding` ClusterRoleBinding, and, for a real domain, the `letsencrypt-prod` ClusterIssuer. Those cluster-scoped resources do not belong to any namespace.

* **The `stackdome-agent` chart**, installed from `oci://quay.io/stackdome/charts/stackdome-agent` at the version selected by `--chart-version`. Before upgrading, the installer server-side applies every CRD shipped by the chart. It then runs `helm upgrade --install` and waits for the chart and operator pods to be ready. If that exact chart version is already deployed, it skips the chart upgrade.
* **A bootstrap Secret**, `stackdome-bootstrap-secrets`, holding the credentials the installer generated: the database password, the JWT signing secret, the encryption key, your admin password, plus your admin email and the domain.
* **RBAC for the API server**: a `stackdome-api-server-account` ServiceAccount, the `stackdome-api-server-role` ClusterRole and binding, and the long-lived `stackdome-api-server-account-secret` token Secret. The role can manage Stackdome stacks, resources, volumes, users, registries, and Postgres addons; observe build and cluster status; create a cluster issuer and CNPG image catalogs; manage the namespaces and Secrets those workloads need; and read pods, logs, Services, nodes, and pod metrics.
* **A `Volume`**, `stackdome-db-data` — 10Gi, `ReadWriteOnce`, storage class `local-path` — backing the platform database.
* **A `Stack`**, `stackdome-platform`, grouping the two resources below.
* **A `db` `StackResource`** running `postgres:16-alpine` on port 5432, not exposed publicly, with the volume mounted at `/var/lib/postgresql/data`.
* **An `api-server` `StackResource`** running the image from `--image`. It runs database migrations as an init step, then serves on port 8000, exposed publicly at your domain.
* **A `ClusterIssuer`** — only when you install on a real domain. See [TLS](#tls) below.

The installer then polls the API server's `/health` endpoint for up to five minutes before continuing.

<Warning>
  The API-server role is narrower than `cluster-admin`, but it is still a cluster-scoped credential with access across namespaces, including read and write access to workload Secrets. The service-account token is long-lived. Anyone who obtains it can exercise every permission in `stackdome-api-server-role`; protect `stackdome-api-server-account-secret` and restrict access to the control-plane namespace.
</Warning>

### In Stackdome

With the API server up, the installer finishes the setup through Stackdome's own API:

* An **admin user** named `Platform Admin`, with the email from `--email` and the generated password.
* An organization named **Default**, with your domain configured on it.
* A cluster named **local** — the server itself — registered with the credentials from the RBAC step, so the platform can deploy to it.
* An **in-cluster image registry** named `default-registry` with 50Gi of backing storage, attached to that cluster. It holds the images Stackdome builds from your git repositories.

<Note>
  The in-cluster registry belongs to the cluster and is created with it. It is not one of the entries on the **Image registries** page — that page is for credentials you add for external registries, and it is empty on a fresh install.
</Note>

## TLS

What the installer does about certificates depends on the shape of your domain. A domain ending in `.nip.io`, `.sslip.io`, `.local`, or `.localhost` is classified as a non-TLS domain; any other non-empty domain is classified as a real one.

### On a `nip.io` or other local-style domain

No `ClusterIssuer` is applied and no Let's Encrypt certificate is requested. The `api-server` resource is created with `tls: false` on its public port and without a cluster-issuer annotation. The chart's Traefik ingress therefore serves its default self-signed certificate.

The final summary still prints an `https://` URL for the dashboard. Because the install configured no certificate for that name, treat a browser certificate warning on a trial install as the expected outcome rather than a sign that something went wrong.

### On a real domain

The installer applies a `ClusterIssuer` named `letsencrypt-prod`, and annotates the `api-server` resource to use it with `tls: true` on its public port. The issuer is configured with:

* ACME server: Let's Encrypt production, `https://acme-v02.api.letsencrypt.org/directory`
* Contact email: the address you passed to `--email`
* Account key stored in a Secret named `letsencrypt-prod-key`
* An HTTP-01 solver, so validation happens over port **80** on your hostname

<Warning>
  The issuer is a `cert-manager.io/v1` resource supplied after the `stackdome-agent` chart is ready. The installer treats a failure to apply it as fatal, so a custom-domain install stops if the cert-manager API is unavailable.
</Warning>

<Note>
  Even once the issuer is applied, the installer does not wait for, or verify, that a certificate was actually issued. If HTTPS on your domain still shows a certificate error after the install finishes, inspect the `ClusterIssuer` and `Certificate` resources in the cluster rather than re-running the installer.
</Note>

## Upgrade an existing install

Use the dedicated upgrade command for a version change:

```bash theme={null}
sudo ./stackdome-install upgrade \
  --image quay.io/stackdome/stackdome:VERSION \
  --chart-version 0.6.11-alpha-rc5
```

`upgrade` requires root, a running local k3s service, a usable `/etc/rancher/k3s/k3s.yaml`, Helm, the `stackdome-agent` release in `stackdome-control-plane`, the bootstrap Secret, and the existing `api-server` resource. It reads the email, domain, database workload type, and secrets from the existing install. If you omit `--image`, it keeps the deployed API-server image. GitHub flags are also accepted and update the stored credentials; values you omit are reused.

## Re-running installation

Re-running with the same flags is handled at every step, and the installer says so as it goes:

| Step                                               | On a second run                                                                                                                                     |
| -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| Port checks                                        | Skipped for 80, 443, and 6443 once k3s is running.                                                                                                  |
| k3s, Helm                                          | Skipped if the binaries exist.                                                                                                                      |
| `stackdome-agent` chart                            | Skipped when the deployed chart is already at `--chart-version`; otherwise its CRDs are applied and Helm upgrades the release.                      |
| Generated secrets                                  | Reused. The installer reads `stackdome-bootstrap-secrets` and, if all four values are present, keeps them — so your admin password does not change. |
| Manifests (RBAC, volume, stack, resources, issuer) | Re-applied declaratively with `kubectl apply`.                                                                                                      |
| Admin user                                         | Login is tried first. Signup only runs if login fails, and if signup fails too the installer waits two seconds and tries login once more.           |
| Organization domain                                | Skipped if that exact domain is already on the organization.                                                                                        |
| Cluster registration                               | Skipped if the organization already has **any** cluster — see the warning below.                                                                    |

<Warning>
  Cluster reuse is not matched to this server. On a re-run the installer lists the organization's clusters and, if the list is not empty, keeps the **first** entry without checking that it points at this machine. On an organization that already has clusters, a re-run therefore adopts an arbitrary existing one and never registers the local cluster.
</Warning>

Two more consequences are worth knowing before you rely on this:

* **Do not delete `stackdome-bootstrap-secrets`.** Reuse depends on it. If it is gone or incomplete, the installer generates a fresh database password, JWT secret, encryption key, and admin password — against a database and an API server that were set up with the old ones.
* **Changing `--domain` or `--image` on a re-run changes the cluster.** The `api-server` resource is re-templated and re-applied with whatever you pass, and a domain that is not already on the organization is added to it. A re-run is not a no-op if the flags differ.
