Installation
This page gets the tools onto your machine and an Octo project onto disk, ready to deploy.
Octo writes Terraform and hands it to Terragrunt to run, so most of the list below is tools your team already has, plus Node.js to run Octo itself.
Prerequisites
| Tool | Version | Why |
|---|---|---|
| Node.js | 20 or newer | Octo is a TypeScript toolchain, and the generated project declares "engines": { "node": ">=20.0.0" }. |
| Terraform or OpenTofu | 1.6 or newer | Creates the infrastructure. The floor comes from terraform.requiredVersion in your definition file, which the scaffolder sets to 1.6.0. |
| Terragrunt | 0.78 or newer | Octo writes one Terragrunt module folder per module and drives them with terragrunt run --all, which arrived in 0.78. |
| AWS credentials | Only if you deploy to AWS. A named profile in ~/.aws/credentials is enough. The AWS local setup guide starts from nothing. |
node --version # v20.x or newer
terraform --version # v1.6.0 or newer, or: tofu --version
terragrunt --version
aws sts get-caller-identity # prints the account you are about to deploy into
Creating a project
There is nothing to install globally. The octo command lives inside @quadnix/octo-build, and the
scaffolder pulls it in as a project dependency:
npx @quadnix/octo-build create-app -t aws-s3-website -n hello-website
cd hello-website
That writes a directory called hello-website, runs npm install, and prints the four commands you
will be running from now on. The -t flag picks which starting definition you get:
aws-s3-website, aws-ecs-server, or aws-dynamodb-service. It changes only octo.yaml and, for
the website template, a folder of files to upload. Everything else in the project is the same
whichever one you pick.
Take aws-s3-website. Static Website deploys it, once you
have put your own account number and a bucket name into it. The shop, the application the rest of
the docs use, is built in a project of its own.
Here is what you get:
hello-website/
├── octo.yaml the definition file: what infrastructure you want
├── package.json four octo:* scripts, one per lifecycle step
├── src/index.ts where your own modules go, empty to start
├── .octo/ Octo's state, and it belongs in version control
├── tsconfig.json
└── website/ only from the aws-s3-website template
Two of those are worth knowing about before you touch anything.
octo.yaml is the definition file. It names the modules your infrastructure is made of and the
inputs each one takes, and it is the only file most people edit. The Definition
File covers it section by section.
.octo/ is where Octo records what it built. It is not a cache: delete it and the next run believes
your infrastructure does not exist, which is why the scaffolded .gitignore keeps it in version
control on purpose. Project Structure covers which files
are committed and why.
The scaffolded .gitignore is a starting point. Change it to suit your team.
Running the CLI
The generated package.json wraps the four lifecycle steps as scripts:
npm run octo:generate # write the Terraform
npm run octo:validate # plan it, and report the plan against your intent
LOCK_ID=$(npm run --silent octo:lock) # apply and commit write shared state
npm run octo:apply -- --lockId $LOCK_ID # create the infrastructure
npm run octo:commit -- --lockId $LOCK_ID # record what was created, and release the lock
Each script points at .octo-tf, so the four steps cannot drift apart. Prefer the scripts to the
binary: npm run puts node_modules/.bin on PATH, and Terraform needs to find octo there when it calls back mid-run.
Running the binary directly works too, as long as you name the output directory yourself:
node_modules/.bin/octo generate -o .octo-tf
Using OpenTofu
Octo does not invoke the Terraform binary; Terragrunt does. Point Terragrunt at tofu through its
own configuration, or per command:
npm run octo:validate -- --terraformBinary tofu
LOCK_ID=$(npm run --silent octo:lock)
npm run octo:apply -- --lockId $LOCK_ID --terraformBinary tofu
The flag sets TG_TF_PATH for that invocation only.
Adding Octo to a project you already have
If you would rather not scaffold, install the packages directly:
npm install @quadnix/octo @quadnix/octo-aws-cdk @quadnix/octo-build @quadnix/octo-event-listeners
| Package | Needed when |
|---|---|
@quadnix/octo | Always. The framework itself. |
@quadnix/octo-build | Always. Provides the octo command. |
@quadnix/octo-aws-cdk | You are deploying to AWS using our modules. |
@quadnix/octo-event-listeners | You want the console output and the journal. The definition file names them by class. |
Install them at the same time and upgrade them the same way: the combination we support is the latest release of each, and Package Versions says what happens when they drift apart.
Your package.json must declare "type": "module", because Octo is ESM only. Calling it from a
CommonJS codebase works, and is covered in
this blog post, but that path is not regularly
tested.
Then write an octo.yaml, and create a directory for state to live in. The Definition
File covers what goes in it.