Self-hosted Enterprise
Run the Enterprise build of Stategraph, ghcr.io/stategraph/stategraph-server, with Docker Compose, connect GitHub or GitLab, and get your first plan and apply on a pull request. For Kubernetes, ECS, and Cloud Run, see Self-hosting. For the free open-source edition, see Self-hosted Open Source.
The Enterprise build needs a license key to complete the first-time setup. Put it in .env as STATEGRAPH_LICENSE_KEY=<key> before the first start, or enter it on the License step of the setup screen. Contact sales for a key. See Editions.
Before you begin
- Docker Engine 20.10 or later, and Docker Compose v2.
- Port 8080 free on the host.
- For Orchestration, a URL that GitHub or GitLab can reach over HTTPS.
- For GitHub, admin rights in the GitHub organization, to create and install a GitHub App.
- For GitLab, gitlab.com or self-managed GitLab 18.1 or later (see GitLab version), a group (personal namespaces are not supported), and the rights to create an access token, add a project webhook, and change the project's CI/CD settings.
Create the Compose project
- Create a directory.
- In it, create
docker-compose.yml:
services:
db:
image: postgres:17-alpine
user: postgres
environment:
POSTGRES_PASSWORD: "stategraph"
POSTGRES_USER: "stategraph"
POSTGRES_DB: "stategraph"
healthcheck:
test: ["CMD", "pg_isready", "-d", "stategraph", "-U", "stategraph"]
interval: 3s
timeout: 3s
retries: 5
volumes:
- db:/var/lib/postgresql/data/
networks:
- stategraph
server:
image: ghcr.io/stategraph/stategraph-server:latest
env_file:
- .env
environment:
DB_HOST: "db"
DB_PORT: "5432"
DB_USER: "stategraph"
DB_PASS: "stategraph"
DB_NAME: "stategraph"
ports:
- "8080:8080"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health/ready"]
interval: 10s
timeout: 5s
retries: 5
start_period: 120s
depends_on:
db:
condition: service_healthy
networks:
- stategraph
networks:
stategraph:
volumes:
db:
- Create
.env:
STATEGRAPH_UI_BASE=http://localhost:8080
STATEGRAPH_UI_BASE, the public URL of the console, is required. All other settings, including Orchestration, go into .env. Leave optional variables unset, not empty: for some, an empty value differs from an absent one.
Start and verify
- Start the services:
docker compose up -d
- Check the containers:
docker compose ps
- Check that Stategraph is ready:
curl -f http://localhost:8080/health/ready
At start, Stategraph migrates the stategraph database, then serves requests. /health/ready returns a non-200 status until the migrations finish. /health/live answers as soon as the web server is up. See Health checks.
First login
Open http://localhost:8080. The first visit shows the setup screen. It asks for the license key first, unless STATEGRAPH_LICENSE_KEY is set in .env. With local authentication, the default, it then creates the first account, with a password of at least 8 characters, and signs you in as the instance admin.
As on Stategraph Cloud, you can connect GitHub or GitLab, or import your state. To connect, you need Orchestration, which the next section turns on. For Google or OIDC sign-in, see Access control.
Enable Orchestration
1. Create the GitHub App or the GitLab application
Run the setup wizard one time, on any computer with a browser:
docker run --rm -p 3000:3000 ghcr.io/stategraph/orchestration-setup:latest
GitHub
- Open http://localhost:3000, and sign in to GitHub when asked.
The wizard creates the App with the permissions, webhook, and credentials that Orchestration needs. Then it shows the settings for .env and the steps to finish on GitHub. Keep that page open until you write .env.
- In the App settings on GitHub, set the webhook URL to
<public-url>/api/github/v1/events, with the webhook secret that the wizard generated. - Register
<public-url>/api/v1/vcs-installations/github/claim/callbackas a callback URL. - Grant the App the organization permission Members: read. Tenant admins can then link an installation from the console.
- Install the App on the organization that holds your repositories.
<public-url> is the value of STATEGRAPH_UI_BASE and STATEGRAPH_OAUTH_REDIRECT_BASE below. To create the App by hand, see Enable Orchestration.
GitLab
The wizard creates nothing in GitLab. It tells you what to create, and collects the values.
- Open http://localhost:3000. When asked how GitLab reaches your server, enter the host name of your public URL.
- Create a dedicated GitLab user for Stategraph.
- As that user, create a personal access token with the
apiscope, and enter it with your GitLab instance URL. - As the same user, in Preferences > Applications, add an application with the
apiscope and the redirect URI that the wizard shows,<public-url>/api/v1/gitlab/callback. Enter its application ID and secret.
The wizard then shows GITLAB_APP_ID, GITLAB_APP_SECRET, and GITLAB_ACCESS_TOKEN for .env. Each GitLab group gets its own access token and webhook secret later, when you connect it in the console.
On self-managed GitLab, see Self-managed GitLab for the two URL variables that the wizard does not write, and the CI template to mirror.
2. Create the Orchestration database and roles
Orchestration keeps its own terrateam database on the same PostgreSQL server. Stategraph reads it through postgres_fdw as the stategraph_mql role. Create both one time in the running db container, with a password that you choose:
docker compose exec db psql -U stategraph -d stategraph \
-c "CREATE DATABASE terrateam OWNER stategraph;" \
-c "CREATE ROLE stategraph_mql LOGIN PASSWORD '<fdw-password>';"
For GitLab, also create the provisioning role, which the console uses to connect a group, with its own password:
docker compose exec db psql -U stategraph -d stategraph \
-c "CREATE ROLE stategraph_provisioner LOGIN PASSWORD '<provisioner-password>';"
Orchestration updates the grants of both roles after every migration. On managed PostgreSQL such as Amazon RDS, the postgres_fdw connection needs superuser rights (rds_superuser). See Enable Orchestration.
3. Write the variables to .env
STATEGRAPH_UI_BASE becomes the public URL, and the TERRAT_* values derive from it. Replace .env with:
STATEGRAPH_UI_BASE=https://stategraph.example.com
STATEGRAPH_OAUTH_REDIRECT_BASE=https://stategraph.example.com
STATEGRAPH_ORCHESTRATION_ENABLED=true
TERRAT_API_BASE=https://stategraph.example.com/api
TERRAT_UI_BASE=https://stategraph.example.com
TERRAT_WEB_BASE_URL=https://stategraph.example.com
STATEGRAPH_FDW_PASSWORD=<fdw-password>
For GitHub, add the settings from the wizard:
GITHUB_APP_ID=<app id>
GITHUB_APP_CLIENT_ID=<oauth client id>
GITHUB_APP_CLIENT_SECRET=<oauth client secret>
GITHUB_WEBHOOK_SECRET=<webhook secret>
GITHUB_APP_URL=https://github.com/apps/<your-app>
GITHUB_APP_PEM=<private key>
For GitLab, add the settings from the wizard and the password of the provisioning role:
GITLAB_APP_ID=<application id>
GITLAB_APP_SECRET=<application secret>
GITLAB_ACCESS_TOKEN=<access token>
STATEGRAPH_FDW_PROVISIONER_PASSWORD=<provisioner-password>
| Variable | Purpose |
|---|---|
STATEGRAPH_OAUTH_REDIRECT_BASE |
Base of the OAuth callback URLs, including the GitHub claim callback. Default http://localhost:<port>, which GitHub cannot reach |
STATEGRAPH_ORCHESTRATION_ENABLED |
Turns on Orchestration and the postgres_fdw connection at start |
TERRAT_API_BASE |
The public URL plus /api, for the runner |
TERRAT_UI_BASE, TERRAT_WEB_BASE_URL |
The public URL. With the same host as STATEGRAPH_UI_BASE, that host serves only the Stategraph console. For the Terrateam console too, give TERRAT_UI_BASE its own host: see Two consoles |
STATEGRAPH_FDW_PASSWORD |
The stategraph_mql password. The server does not start without it |
GITHUB_APP_ID, GITHUB_APP_PEM |
The App ID and private key, which the wizard writes on one line with \n for line breaks |
GITHUB_APP_CLIENT_ID, GITHUB_APP_CLIENT_SECRET |
The App's OAuth client. Also verifies the GitHub identity of a tenant admin who links an installation |
GITHUB_WEBHOOK_SECRET |
Required with GITHUB_APP_ID, or Orchestration does not start |
GITHUB_APP_URL |
The App's install page, linked from the console |
GITLAB_APP_ID, GITLAB_APP_SECRET, GITLAB_ACCESS_TOKEN |
Turn on GitLab in Orchestration. When GITLAB_APP_ID is set, the other two are required |
STATEGRAPH_FDW_PROVISIONER_PASSWORD |
The stategraph_provisioner password. Without it, the console cannot connect a GitLab group |
TERRAT_DB_* need no setting: Orchestration uses the DB_* values with the database name terrateam. Telemetry is on by default. To turn it off, set TERRAT_TELEMETRY_LEVEL=disabled. All variables are in Environment variables.
4. Restart
Restart, and follow the server log:
docker compose up -d
docker compose logs -f server
Orchestration migrates the terrateam database at start. If Orchestration does not start, the log gives the cause, usually a missing GitHub webhook secret. A missing GitLab variable logs a CONFIG : ERROR line that names it.
Your first pull request
From here, the flow is the same as on Stategraph Cloud, which has the GitHub and GitLab steps, the workflow and pipeline files, the example repository, and what to check when no plan starts. Runs execute on your GitHub Actions or GitLab CI runners. The server only dispatches them.
GitHub
- In the console, open Get Started > Set up your first repository > Connect GitHub, and claim your installation:
- An instance admin sees every unclaimed installation.
- A tenant admin who is not an instance admin verifies with GitHub first, and sees only installations on organizations that they administer.
- Pick a repository that the App can access, or fork the example repository.
- Add
.github/workflows/terrateam.ymlto the default branch. - Open a pull request that changes a
.tffile. Your GitHub App posts the plan comment. - Comment
stategraph apply, then merge.
GitLab
- In the console, open Get Started > Set up your first repository > Connect GitLab. This needs admin rights on the tenant.
- Enter the GitLab URL and group, the repository, and an access token with the
apiscope. In GitLab, add the project webhook to<public-url>/api/v1/gitlab/eventswith the secret that the wizard shows one time, and set the minimum role to use pipeline variables to Developer. - Add the
.gitlab-ci.ymlthat the wizard shows to the default branch. - Create a branch from the default branch, change a
.tffile, and open a merge request. The owner of the group's access token posts the plan comment. - Comment
stategraph apply, then merge.
Next steps
- Self-hosting: other deployments and upgrades.
- Enable Orchestration: manual setup, GitLab credential rotation, self-managed GitLab, and managed PostgreSQL.
- Environment variables: all settings that Stategraph reads.
- Access control: Google, OIDC, tenants, and API access tokens.
- Import your state: use Infrastructure as a Database on this server.