On-ramp: From bare Terraform + GitHub Actions

This is the on-ramp for the silent majority of Terraform shops: one repo, state in S3 (with a DynamoDB lock or S3 native locking), and a GitHub Actions workflow that runs plan-on-PR and apply-on-merge. No TFC, no Atmos, no Atlantis, no Spacelift — just the basics.

If that's your stack, nothing about it changes. Terrantula sits on top of it. This guide shows you how.

Where you are now

your-tf-repo/ ├── terraform/ # your modules ├── tenants/ # or wherever your per-tenant config lives ├── .github/workflows/ # your plan-on-PR + apply-on-merge workflow └── (state in S3)

You run terraform apply from GitHub Actions. State lives in S3. Adding a tenant means hand-editing a config file and waiting for the PR to merge. You have no fleet-wide view — nobody can answer "what do we actually have, and how does it connect?"

What Terrantula adds

Terrantula is the fleet layer above your Terraform. It doesn't run Terraform and it doesn't replace it — it orchestrates the population your Terraform produces:

  • Visibility first. A graph of every entity in your fleet, the cells they're placed in, and how they connect — derived from your existing state.
  • Placement and constraints. Typed entities with capacity ceilings and cardinality-checked relationships, so "we cap at 50 tenants per cluster" stops living in a runbook.
  • Lifecycle and audit across hundreds of instances.
Terrantula never runs

terraform apply When you trigger an Action, Terrantula opens a pull request against your existing repo — committing or patching the IaC files that describe the change. Your GitHub Actions workflow applies the merged PR, exactly as it does today. Terrantula never executes terraform applyand never hosts your runners or your state.

Step 1 — Get a view, with no server and no signup

Start with pure visibility. The CLI runs in local mode out of the box — everything stored in a local SQLite database, nothing leaves your machine. Point it at your state and open a dashboard:

terrantula import terraform --state ./terraform.tfstate --config terrantula.yaml
terrantula dashboard

The --state flag also reads directly from S3, so you don't have to pull state down first:

terrantula import terraform --state s3://my-tf-state/prod/terraform.tfstate --config terrantula.yaml

Repeat --state to merge several state files into one fleet view:

terrantula import terraform \
  --state s3://my-tf-state/us-east-1/terraform.tfstate \
  --state s3://my-tf-state/us-west-2/terraform.tfstate \
  --config terrantula.yaml

The --config file is a YAML mapping that says which Terraform resources become which Terrantula entity types and relationships. Want to see what would be imported before writing anything? Add --plan (an alias for --dry-run, for terraform-plan muscle memory):

terrantula import terraform --state ./terraform.tfstate --config terrantula.yaml --plan

That's the whole first-five-minutes loop. For the step-by-step walkthrough, including the starter configs that map common AWS/Kubernetes resources to entity types, follow the Quick Start.

Step 2 — Keep imports current

Re-running the import re-syncs the graph from your latest state. When a tenant has been torn down in Terraform and you want Terrantula to forget it too, add --replace to delete Terraform-managed entities that are no longer present in the supplied state:

terrantula import terraform --state ./terraform.tfstate --config terrantula.yaml --replace

You can wire this into your existing GitHub Actions workflow as a post-apply step — drive it from environment variables so there's no saved login in CI:

export TERRANTULA_BASE_URL=https://api.example.com
export TERRANTULA_TOKEN=terr_xxxxxxxx
terrantula import terraform --state s3://my-tf-state/prod/terraform.tfstate --config terrantula.yaml

Step 3 — Add orchestration when you're ready (optional)

Visibility is enough to start. When you grow into a real cattle problem — placement, capacity, templated onboarding — you turn on Actions. For bare-TF + GitHub Actions, the Action uses the pull-request trigger:

  1. You fire an Action (e.g. OnboardTenant). Terrantula validates parameters, enforces constraints, and picks the right cell.
  2. Terrantula opens a PR against your repo — committing a new per-tenant file (e.g. tenants/acme.tfvars.json), or patching an existing aggregate file with a JSON-array-append.
  3. A reviewer merges the PR — your normal review workflow.
  4. Your existing GitHub Actions workflow runs terraform apply against the merged change.
  5. Optionally, a reference workflow (terrantula-tf-dispatch.yml) signals back to Terrantula when the apply finishes, so the entity's lifecycle advances to active.

Setup is a drop-in: add the reference workflow, add a callback-token secret, apply your Terrantula catalog. No new infrastructure to operate. See the canonical demos for the exact shape — bare-tf-gh-actions-onefile-per-tenant (one file per tenant) and the module-per-tenant variant.

What stays the same

  • Your runner. GitHub Actions keeps running terraform apply. Terrantula never touches it.
  • Your state. It stays in S3. Terrantula reads state; it never hosts it.
  • Your CI and review workflow. Changes still arrive as PRs your team reviews and merges.
  • Your credentials. AWS creds stay in your CI, exactly where they are.

Terrantula adds the placement decision, constraint enforcement, lifecycle tracking, and the fleet view — nothing else moves.

Next

  • Quick Start — import your state and open the dashboard end-to-end.
  • FAQ — does it run Terraform, is it self-hostable, which substrates.
  • Triggers reference — the pull-request trigger and the others, in detail.
  • Examples — the bare-TF + GitHub Actions canonical demos.