Terraform
HostStack ships an open-source Terraform provider (see packages/terraform-provider-hoststack) — covered at the bottom of this page. Alongside it, the other supported provisioning surfaces are hoststack.yaml, the TypeScript SDK, and the CLI. Pick the one that matches how you already provision the rest of your stack.
Decide which surface to use
hoststack.yaml
Committed alongside your code. Declares runtime, build, env, scaling, disk. Read on every deploy — the closest thing HostStack has to a “manifest”. Best for per-service shape.
TypeScript SDK
Imperative, fully typed. Use from a setup script when you need to create a project + services + databases + env vars + domains in one shot.
CLI
hoststack <cmd>. Same API surface as the SDK, no code to write. Best for one-off provisioning from a Makefile or shell script.
SDK-based provisioning script
The closest analogue to terraform apply today is a short script that calls the SDK. The SDK is idempotent at the level of create calls — hitting create for a service that already exists returns a 409 you can catch. The pattern below is what we use internally to bring up demo environments.
import { HostStack, ConflictError } from '@hoststack.dev/sdk';
const client = new HostStack({ apiKey: process.env.HOSTSTACK_API_KEY! });
const TEAM = process.env.TEAM_ID!; // numeric or team_… publicId
async function getOrCreateProject(name: string) {
try {
const { project } = await client.projects.create(TEAM, { name });
return project;
} catch (err) {
if (err instanceof ConflictError) {
const { projects } = await client.projects.list(TEAM);
return projects.find((p) => p.name === name)!;
}
throw err;
}
}
const project = await getOrCreateProject('billing');
await client.services.create(TEAM, {
projectId: project.id,
name: 'api',
type: 'web_service',
runtime: 'bun',
buildCommand: 'bun install && bun run build',
startCommand: 'bun run start',
});
await client.databases.create(TEAM, {
projectId: project.id,
name: 'app-db',
engine: 'postgres',
plan: 'starter',
});Run it once to bring the stack up; subsequent runs are safe because theConflictError branch skips creates that already happened. State lives in HostStack itself — there is no .tfstate file to manage or lock.
CLI-based provisioning
If a Bash script is closer to your existing tooling, the CLI exposes the same surface. Every command accepts --json for machine-readable output and resolves prj_… / svc_… publicIds wherever a numeric id is normally accepted.
#!/usr/bin/env bash
set -euo pipefail
hoststack login --key "$HOSTSTACK_API_KEY" # caches the key in ~/.hoststack/config.json
PROJECT=$(hoststack projects create --name billing --json | jq -r '.publicId')
hoststack services create \
--project "$PROJECT" \
--name api \
--type web \
--runtime bun \
--build "bun install && bun run build" \
--start "bun run start"
hoststack db create \
--project "$PROJECT" \
--name app-db \
--engine postgres \
--plan starterPer-service shape: hoststack.yaml
Once a service exists, its runtime + build + env shape lives in a hoststack.yaml committed alongside the code. The file is read on every deploy, so changes to it ship like any other code change. Provisioning script + manifest file together cover the use-cases Terraform usually owns:
- Creating resources — the SDK / CLI script.
- Configuring resources —
hoststack.yaml. - Secrets — set with
hoststack env set KEY=value --secret, then referenced (without value) in the YAML.
The Terraform provider
HostStack ships a first-party, open-source Terraform provider in the repo with five resources — hoststack_project, hoststack_service, hoststack_database, hoststack_domain, and hoststack_env_var. It requires Terraform 1.5+ and reads credentials from HOSTSTACK_API_KEY / HOSTSTACK_TEAM_ID (and an optional HOSTSTACK_API_URL).
terraform {
required_providers {
hoststack = {
source = "hoststack/hoststack"
version = "~> 0.1"
}
}
}
provider "hoststack" {
# api_key + team_id come from HOSTSTACK_API_KEY / HOSTSTACK_TEAM_ID
}
resource "hoststack_project" "app" {
name = "my-app"
}
resource "hoststack_service" "api" {
project_id = hoststack_project.app.id
name = "api"
type = "web_service"
runtime = "node"
start_command = "npm start"
auto_deploy = true
}
resource "hoststack_database" "postgres" {
project_id = hoststack_project.app.id
name = "main-db"
engine = "postgres"
plan = "starter"
}The provider is not yet published to the Terraform Registry, so for now you build it from source (go build in packages/terraform-provider-hoststack) and wire it up with a dev override. See the package README for the full attribute reference and a runnable examples/ config. If you need a registry release for an internal platform, open a ticket — it helps us prioritise.
Next: Infrastructure as Code · SDK Reference · CLI Reference