ackee
Docs

How Ackee works

You draw nodes and connect them with typed edges. Ackee compiles the graph to readable OpenTofu, shows you the plan, and applies it in your own cloud account — AWS, Google Cloud, or Azure. This page explains every node and every feature.

Drawnodes + typed edges
Compilegraph → OpenTofu
Planevery change, color-coded
Approveyou read the diff
Applyyour account, live logs

The graph model

An environment holds one graph, and a graph targets one cloud — the palette only shows one provider's nodes at a time, and the compiler rejects a mix. Every node declares typed ports; an edge can only connect an out-port to an in-port of the same kind, enforced identically in the editor and in the compiler.

When a graph compiles, each node produces its cloud resources plus two interfaces: exports (values other nodes may read — a hostname, an ARN, a service account) and slots (holes the node cannot fill alone — which VPC am I in?). Edges read exports and fill slots. That is why node compilers stay ignorant of each other: a Service never needs to know a VPC exists.

Config keys are strict: a typo in a config key fails compilation loudly instead of silently deploying a default. Cloud resource names are deterministic — where a name must be globally unique (buckets, storage accounts), the node appends a hash suffix derived from the node's identity, so the same graph never produces a different name on a later apply.

Edge kinds

one line, several resources — the typed edge is the product
vpc ──network──▶ servicePlaces the target inside the source network. Fills whatever the target declares: VPC and subnets on AWS, a direct VPC interface on Cloud Run and its workers and jobs, the private peering for Cloud SQL and AlloyDB, the Private Service Connect policy for Valkey, resource group and delegated subnets on Azure. Drawn from a Static egress node instead of the Network, it also routes the service's outbound traffic through the NAT.
service ──env──▶ postgresInjects connection details into the service's environment (MAINDB_HOST, MAINDB_PORT, …), prefixed by the target's name. Passwords ride the platform's secrets channel, resolved at container start — never a literal in the plan or, where the cloud allows it, in state.
service ──iam──▶ bucketGrants the service's own identity least-privilege access to the target: its task role on AWS, its dedicated service account on Google Cloud, its managed identity on Azure. Per-target policy templates, never allow-everything-on-this-ARN — object user on a bucket, subscriber and publisher on a queue, writer on a registry, and on Firestore a project role pinned by condition to that one database.
lb ──traffic──▶ serviceWires whatever sends requests to whatever receives them. The edge dispatches on the destination: load balancer → service routes requests, load balancer → domain writes the DNS record, load balancer → certificate adds the HTTPS listener, load balancer → firewall attaches the firewall's policy, cron → service grants the invoker role, cron → job starts a run through the Cloud Run Admin API, and queue → function makes the function the queue's consumer — Lambda polling SQS, Eventarc pushing Pub/Sub.

On AWS, a single env edge from a service to a database compiles to four things: the environment variables, an ECS secret reference for the password, the IAM policy to read that secret, and a security group ingress rule from the service to the database. On Google Cloud there are no security groups to write — private IP inside one network is already reachable — so the same edge is variables, a Secret Manager reference, and the accessor grant.

Plan, approve, deploy

Nothing applies until you approve a plan. Ackee runs the compiled OpenTofu and shows every pending change on the canvas and in the plan view, with live logs streaming through apply. Roughly 1 in 12 men has a red–green color deficiency, and here the difference between create and destroy is a new database versus a deleted one — so every state is encoded four times: symbol, word, border pattern, and color. Create is solid, update dotted, replace double, destroy dashed, so the diff still reads correctly in greyscale even where only a border carries it.

createupdatereplacedestroyno-op

Replace shares destroy's red because it is destructive — on a database it destroys the instance and builds an empty one; the word is what separates them. The plan page lists every change by the card you drew, destroys first, with each resource's fields one click away and the ones that force a replacement marked. It also says what else a replace or destroy reaches through the connections you drew. Approving a plan that only builds is one click; one that replaces or destroys a database or a bucket asks you to type that card's name first. Destroying everything is planned from the deployments page, behind the project's name, and stops for your approval like any other plan. Deleting a project requires typing its name.

Deploy on push

Connect a GitHub repository to a whole project and a push redeploys the graph. Or connect a repository to one compute node — an ECS Service, a Cloud Run service, a Container App, a Worker, a Job — and a push redeploys only that node's image, independent of everything else in the project. Every container node across the three clouds shares the same image field, so it is one implementation, not three — an app with one service on AWS and another on Google Cloud wires both to their own repos in the same place.

Auto-apply is optional — without it a push produces a plan waiting for your approval. Ackee sets commit statuses on the pushed commit and keeps a delivery log that explains every push it declined. Signing in with GitHub (or connecting GitHub from your account page) is what lets these screens list your repositories instead of asking you to type owner/name.

Workers, jobs, functions and crons

Google Cloud gives a container three ways to run, and Ackee draws them as three nodes. A Cloud Run service answers HTTP and scales on requests. A Worker is a Cloud Run worker pool: instances that stay up and pull work from a Queue, with no URL and no request-driven scaling — you set the instance count, and zero means paused. A Job is a Cloud Run job: a container that runs to completion with its own timeout, retries and task count.

A Cron is only a clock with an identity. Its traffic edge decides what the tick does. Pointed at a service, Scheduler sends an HTTP request with an OIDC token minted for that service's URL, so a private service accepts its own cron and nothing else. Pointed at a Job, Scheduler calls the Cloud Run Admin API to start a run, which takes an OAuth token instead — the one place the two targets differ, and a mistake that would otherwise look exactly like a missing role. The compiler picks the right token from the edge; you never choose.

A Function is the fourth container node: a Cloud Run function built by Google from a zip in a bucket. Call it over HTTP, or draw a traffic edge from a Queue and it runs per message through Eventarc — a consumer with no loop to write. AWS mirrors all of this with two nodes: a Function on Lambda, from a container image or a zip, and a Schedule on EventBridge Scheduler that invokes it on a rate, a cron, or once at a given time. An SQS queue's traffic edge to a Lambda Function creates the event source mapping that makes the function its consumer.

Which to draw: a queue consumer or anything that loops forever is a Worker; a nightly report, a migration, or a backfill is a Job; a handler that runs per message or per request with nothing resident is a Function; only something that has to answer requests all day is a service.

Databases and caches

Six Google Cloud databases and caches, one env edge for all of them. Every database hands the connected service the same variables —NAME_HOST, NAME_PORT, NAME_NAME,NAME_USER — and its password as a Secret Manager reference resolved when the container starts, never a literal in the plan.

Cloud SQL · Postgres or MySQLThe default. A private-IP instance on the network's peering, with a generated password that never enters Terraform state — the compiler writes it straight into Secret Manager and the database user through write-only attributes. MySQL is the same node with the engine swapped: port 3306 and a utf8mb4 database.
AlloyDBPostgres for workloads that outgrow Cloud SQL, on the same peering and the same env edge. The trial switch makes it a 30-day free cluster. One honest difference: the provider has no write-only password for AlloyDB yet, so this password does land in state — the container still only ever sees a secret reference.
FirestoreServerless: no VPC, no instance to size, pay per operation. Always a named database, never the project's default one. Enterprise edition speaks the MongoDB wire protocol and exports a connection string that authenticates as the service's own identity, so there is no password at all. Access is an iam edge, not a network edge.
Redis · ValkeyBoth hand the service one CACHE_URL in the redis:// form every client understands. Redis rides the network's private peering. Valkey, Google's recommended engine for new work, connects over Private Service Connect instead, which needs the Network node's "Allow Memorystore for Valkey" switch — the compiler tells you if it is off.

The queues live in the same category: Pub/Sub and Cloud Tasks on Google Cloud, SQS on AWS — each holds work between a producer and a consumer, and each node card below spells out which edges wire them. Artifact Registry sits beside these as storage: a private Docker repository. Pulling from it needs no edge at all — Cloud Run's own service agent already reads every repository in the project — so an iam edge to it means something stronger, that the workload publishes images, and grants writer.

Raw resources

when no bundled node covers it
4,156 types

The bundled nodes are opinions — a database whose password never enters Terraform state, a bucket that blocks public access, a network that has already made its private-services peering. The Raw resource node is the other trade: exactly the one resource you name, none of those defaults, and the only way to reach the resource types no bundled node covers. It is one node type per cloud rather than a generated node per resource — the resource type is a field, so the palette stays navigable and a graph that uses it is still an ordinary graph.

AWS1,715resource typeshashicorp/aws ~> 6.19
Google Cloud1,337resource typeshashicorp/google ~> 8.1
Azure1,104resource typeshashicorp/azurerm ~> 5.4

The catalogues above are generated from each pinned provider's own schema, so the palette offers exactly what the provider in this build accepts. They list type names only: the arguments are the provider's to validate, at plan time, against its own documentation.

A raw node arrives wired to nothing — all four port kinds in both directions, and no meaning attached to any of them yet. Four fields give it one:

Resource typeThe provider's own name for it — aws_sqs_queue, google_bigquery_dataset, azurerm_signalr_service. Picked from the catalogue, and the only required field.
Arguments · Nested blocksTwo fields rather than one because HCL treats them differently and the provider will not accept either for the other: a security group's ingress is a block, its tags an object-valued argument, and both look like a JSON object here. Blocks go in as [{ "type": "ingress", "attrs": {…} }], repeated for repeated blocks.
Exports · SlotsThe interface every node has, declared by hand instead of built in. An export maps a name others may read to an attribute of this resource (url → url); a slot maps a name an edge may fill to one of its arguments (vpcId → vpc_id). Until you declare them, nothing can connect.
IAM actionsNo template exists for a resource type chosen at drawing time, so an iam edge needs the grant spelled out — a list of actions on AWS, a built-in role name on Azure, and on Google Cloud the additive _iam_member resource that carries the role. Never an authoritative binding.

What you give up is worth stating plainly. No slot is ever required, because nothing here knows which of a given resource's arguments are mandatory — the provider catches a missing one at plan time. The permission preflight and the enabled-API check are keyed on resource type, so they cannot vouch for a raw resource: if the project's credentials cannot create it, the apply is where you find out. And cv in the console opens no page for one, because its type is not known until you pick it. Everything else — the plan diff, approval, the deployment log — is identical.

The full field list and each cloud's edge rules are on the node cards below: Raw resource · AWS, Raw resource · Google Cloud, Raw resource · Azure.

Presets

every starter graph, and when to reach for it

A new project starts from a blank canvas or a preset — 17 starter graphs on Google Cloud and 17 on AWS and 16 on Azure. A preset lands on the canvas pre-wired and fully editable, exactly as if you had dragged each node from the palette; nothing deploys until you plan and approve. Every preset is compile-tested: the test suite compiles each one with the real node registry, so the editor can never offer a template the compiler would reject, and a preset that stops compiling fails the build, not the first user who picks it. They are also browsable in the template gallery.

AWS

17 presets
Web service2 nodes
NetworkService

A container on ECS Fargate with its own VPC and public subnets.

When to reach for it

The smallest AWS deploy: a container on ECS Fargate in its own VPC, reachable on its public IP. Use it for a staging service or a first deploy — every bigger AWS preset is this plus more nodes. Unlike Cloud Run there is no platform ingress: the service's own security group opens the container port, and when a stable address starts to matter, the public-website preset adds the load balancer in front.

Web service + Postgres3 nodes
NetworkServicePostgres

ECS Fargate and a private RDS Postgres, with the connection details injected as environment variables.

When to reach for it

The standard CRUD app on AWS: Fargate and an encrypted RDS Postgres that is never publicly reachable. Use it when your data is relational and you want RDS's managed answer — the one env edge compiles to the variables, the ECS secret reference for the password, the IAM grant to read it, and the security-group rule from service to database, so the app just reads MAINDB_HOST and connects. AWS generates and rotates the password in Secrets Manager; it never enters Terraform state.

Full web app5 nodes
NetworkServicePostgresBucketSecret

ECS Fargate with Postgres, a private assets bucket, and an API-key secret — every connection wired.

When to reach for it

The production starter: Postgres, a private S3 bucket with public access blocked on all four counts, and a Secrets Manager secret for the third-party API key. Use it when the app will clearly need file storage and outside credentials from day one. The secret is created empty — set its value in the AWS console or CLI, and ECS resolves it when the container starts, so it is never a literal in the plan.

Public website5 nodes
NetworkServiceLoad BalancerDomainCertificate

ECS Fargate behind an Application Load Balancer with an ACM certificate and your own domain — edit the zone and hostname to yours.

When to reach for it

The production front door on AWS: an Application Load Balancer with an ACM certificate and your Route 53 domain, in front of the service. Use it the moment a real hostname matters — the certificate validates by DNS inside the same apply, with no paste-this-record step, and the alias record lands in your zone. Edit the hosted zone ID and hostname to yours; the domain must already be delegated to Route 53 in this account.

Protected site4 nodes
NetworkServiceLoad BalancerFirewall (WAF)

The load-balanced service behind AWS WAF — a per-IP rate limit and AWS's managed rule groups on the Application Load Balancer.

When to reach for it

The load-balanced service with AWS WAF in front: a per-IP rate limit and AWS's Common and Known-Bad-Inputs managed rule groups. Use it for anything public that accepts input or gets scraped — the rate rule blocks an IP over the limit per five minutes, and the managed groups answer Log4j-style probes before they reach the service. Add the Domain and Certificate nodes from the public-website preset when the hostname matters too.

Event-driven function4 nodes
NetworkServiceQueueFunction

A public API publishing to SQS and a Lambda function invoked per batch through an event source mapping — a consumer with no loop to write.

When to reach for it

A queue consumed by a function: the API publishes to SQS and Lambda polls it through an event source mapping, invoking the function per batch of ten with partial-batch failures reported. Use it when the consumer is small enough that a resident worker is waste. Failed messages redrive to the dead-letter queue after five receives instead of retrying forever, and the queue's visibility timeout ships at six times the function's timeout — AWS's own advice, pre-applied. Swap the placeholder code bucket for the zip your build uploads.

Scheduled function2 nodes
ScheduleFunction

EventBridge Scheduler invoking a Lambda function on a cron schedule — swap the placeholder code bucket for yours.

When to reach for it

EventBridge Scheduler invoking a Lambda function on a cron — the nightly report, the cleanup pass, the recurring sync. Use it for recurring work that fits Lambda's model: the schedule runs as its own role with invoke on exactly this function, retries are capped at three so one bad deploy is not a day of identical failures, and nothing in the graph is publicly callable. The 3 a.m. cron is a placeholder; rate(1 hour) and one-shot at(…) forms work too.

Static site2 nodes
FunctionStatic site

A private bucket served through CloudFront, with a Lambda function that uploads the site on each publish.

When to reach for it

A marketing page or docs site: a private bucket behind CloudFront, reached only through the distribution, with a Lambda function that uploads the site. Use it when nothing runs server-side — 403s and 404s rewrite to the error document with a 200, which is what a single-page app's router needs, and the publisher function is where your deploy step drops the built files. Served at the distribution's cloudfront.net name; a custom domain needs a us-east-1 certificate Ackee does not compile yet.

Transactional email2 nodes
FunctionEmail domain

A sending domain on SES with DKIM, SPF and DMARC records written into your zone, and a Lambda function granted send on that identity only.

When to reach for it

A sending domain on SES with every deliverability record — 2048-bit DKIM, a custom MAIL FROM subdomain with its MX and SPF records, and DMARC at p=none — written into your Route 53 zone in one apply. Use it when the product starts sending receipts, invites or resets: the function's iam edge grants SendEmail on this identity only, never ses:*. Remember a new AWS account starts in the SES sandbox — mail reaches only verified addresses until AWS grants production access.

Web app + cache4 nodes
NetworkServicePostgresRedis

ECS Fargate with private RDS Postgres and ElastiCache Redis — sessions and hot reads beside the database.

Web app + Valkey4 nodes
NetworkServicePostgresValkey

ECS Fargate with private RDS Postgres and ElastiCache for Valkey — Redis-compatible, at a lower price per node.

Web service + Aurora3 nodes
NetworkServiceAurora Postgres

ECS Fargate and an Aurora Postgres cluster on the private network, starting at a single instance.

Web service + MySQL3 nodes
NetworkServiceMySQL

ECS Fargate and a private RDS MySQL instance, with the connection details injected as environment variables.

Worker + queue4 nodes
NetworkServiceWorkerQueue

An API publishing to SQS and a Fargate worker consuming it — the queue keeps a traffic spike from becoming a database spike.

Scheduled job3 nodes
CronNetworkJob

EventBridge Scheduler starting an ECS task on a cron schedule — a container that runs to completion, with its own task role.

Serverless app4 nodes
NetworkServiceDynamoDBBucket

ECS Fargate with DynamoDB and a private S3 bucket — no database instance to size, pay per request.

Private image registry3 nodes
NetworkWorkerImage Registry

A Fargate worker that builds and pushes to a private ECR repository — the registry your other services pull from.

Google Cloud

17 presets
Web service2 nodes
NetworkCloud Run

A container on Cloud Run with its own private network.

When to reach for it

The smallest thing that deploys, and the right first graph for almost any project: point the image at your container and you have a URL with HTTPS, scaling and logs. Use it for a staging service, an internal tool, or the first deploy of an app whose database comes later — every other web preset is this one plus more nodes, so nothing is wasted by starting here. The network is not decoration: Cloud Run's direct VPC egress requires it, and it is the network your future database will share.

Web service + Postgres3 nodes
NetworkCloud RunCloud SQL

Cloud Run and a private Cloud SQL Postgres, with the connection details injected as environment variables.

When to reach for it

The standard CRUD app: an API with its own Postgres, private IP only. Use it when your data is relational and you want the boring, default answer — the service finds MAINDB_HOST, MAINDB_USER and the password already in its environment, with the password living in Secret Manager, never in Terraform state. The db-f1-micro tier is a development size; raise the tier field for production and the instance resizes in place.

Full web app5 nodes
NetworkCloud RunCloud SQLBucketSecret

Cloud Run with Postgres, a private assets bucket, and an API-key secret — every connection wired.

When to reach for it

The production starter: database, a private bucket for uploads or generated files, and a secret for the third-party API key every real app eventually has. Use it when you know the app will need file storage and outside credentials — picking it first costs nothing over web-db and saves drawing the same three edges later. The secret is created empty: set its value in the Google console or CLI, and Cloud Run resolves it at container start.

Web app + cache4 nodes
NetworkCloud RunCloud SQLRedis

Cloud Run with private Cloud SQL Postgres and Memorystore Redis — sessions and hot reads beside the database.

When to reach for it

Web-db plus Memorystore Redis. Use it when sessions, rate counters or hot reads are hitting the database on every request — the service gets one CACHE_URL in the redis:// form every client library accepts, over the same private network as Postgres. A session store on the database is the usual first scaling complaint; this is that complaint pre-answered.

Worker + queue4 nodes
NetworkCloud RunWorkerQueue

A public API publishing to Pub/Sub and a worker pool consuming it — the queue keeps a traffic spike from becoming a database spike.

When to reach for it

The produce/consume pair: a public API publishing to Pub/Sub and a worker pool pulling from it. Use it when requests trigger work that should not happen inside the request — sending mail, generating exports, calling slow third parties — because the queue absorbs a traffic spike that would otherwise become a database spike. The worker is a Cloud Run worker pool: no URL, no request scaling, instances you count by hand, and zero means paused.

Scheduled job3 nodes
CronNetworkJob

Cloud Scheduler starting a Cloud Run job on a cron schedule — a container that runs to completion, authenticated with its own identity.

When to reach for it

Cloud Scheduler starting a Cloud Run job on a cron. Use it for the nightly report, the cleanup pass, the recurring import — anything that runs to completion and must not be squeezed into an HTTP request deadline. The job carries its own timeout, retries and task count, and Scheduler starts it through the Cloud Run Admin API with its own identity, so nothing here is publicly callable.

Public website4 nodes
NetworkCloud RunHTTPS load balancerDomain

Cloud Run behind a global HTTPS load balancer with a managed certificate and your own domain — edit the zone and hostname to yours.

When to reach for it

The production front door: your domain on a global HTTPS load balancer with a Google-managed certificate, in front of Cloud Run. Use it the moment a real hostname matters — Cloud Run's own domain mapping is still a preview feature, and this chain is Google's supported answer. Edit the zone and hostname to yours before deploying; until DNS points at the exported IP the certificate serves as a placeholder, then self-heals.

Serverless app4 nodes
NetworkCloud RunFirestoreBucket

Cloud Run with Firestore and a private bucket — no database instance to size, pay per operation.

When to reach for it

Cloud Run with Firestore and a bucket — no database instance to size, back up or pay for while idle. Use it for spiky or small workloads, document-shaped data, or a prototype you want to cost nothing between visits: Firestore bills per operation and Cloud Run scales to zero. Access is IAM rather than network — the service's own identity reads the database, so there is no password anywhere in the graph.

Web service + AlloyDB3 nodes
NetworkCloud RunAlloyDB

Cloud Run and an AlloyDB Postgres cluster on the private network, started as a free 30-day trial.

When to reach for it

Web-db with AlloyDB in Cloud SQL's place — same private peering, same env edge, so your app cannot tell the difference. Use it when Postgres on Cloud SQL is the bottleneck: AlloyDB is Google's bigger engine, roughly a 39% premium at list price. The preset starts it as a 30-day free trial cluster, which is the cheap way to find out whether the engine earns that premium on your workload before committing.

Web app + Valkey4 nodes
NetworkCloud RunCloud SQLValkey

Cloud Run with private Cloud SQL Postgres and Memorystore for Valkey — Google's recommended cache engine, over Private Service Connect.

When to reach for it

Web-cache with Valkey in Redis's place — Google's recommended cache engine for new work. Use it for a new project where you have no Redis habits to preserve; every ordinary Redis client connects to it unchanged. The one structural difference is networking: Valkey rides Private Service Connect, so this preset's network node ships with the "Allow Memorystore for Valkey" switch already on.

Web service + MySQL3 nodes
NetworkCloud RunMySQL

Cloud Run and a private Cloud SQL MySQL instance, with the connection details injected as environment variables.

When to reach for it

Web-db with the engine swapped. Use it when the framework, the team or the imported schema speaks MySQL — same private IP, same env edge, same password handling as the Postgres preset, with the database created as utf8mb4 rather than MySQL's three-byte legacy charset, so an emoji in a comment field is not your first production incident.

Private image registry3 nodes
NetworkWorkerArtifact Registry

A worker pool that builds and pushes to a private Artifact Registry repository — the registry your other services pull from.

When to reach for it

A worker pool with push access to a private Artifact Registry — the shape of a self-hosted build runner. Use it when images should be built and published inside your own project rather than a third-party CI: the env edge hands the builder the repository path, the iam edge grants writer, and every other service in the project can already pull from the registry with no edge at all.

Task queue4 nodes
NetworkCloud RunTask queueCloud Run

A public API enqueuing to Cloud Tasks and a private Cloud Run handler receiving each task as an authenticated request — rate limits and retries live in the queue.

When to reach for it

Cloud Tasks between two services: the API enqueues, and Google delivers each task as an authenticated HTTP request to a private handler. Use it over Pub/Sub when each item is a call to make rather than a stream to drain — the rate limit, retries and backoff live in the queue instead of your code, and the handler stays private because the queue calls it as its own identity, which nothing else holds.

Static egress IP3 nodes
NetworkStatic egress (NAT)Cloud Run

A Cloud Run service behind Cloud NAT, so every outbound request leaves from one reserved IP a partner can allowlist.

When to reach for it

A service whose outbound requests all leave from one reserved IP. Use it when a partner API, a bank, or a customer's firewall allowlists source addresses — the NAT sits between the network and the service, and the address survives redeploys because it is reserved, not ephemeral. Databases stay on the plain network node; only the service's egress routes through the NAT.

Event-driven function4 nodes
NetworkCloud RunQueueFunction

A public API publishing to Pub/Sub and a Cloud Run function invoked per message through Eventarc — a consumer with no loop to write.

When to reach for it

A queue consumed by a function: the API publishes to Pub/Sub and Eventarc invokes a Cloud Run function per message. Use it when the consumer is small enough that a resident worker is waste — a notification sender, a thumbnail maker, a webhook fanout. Retries come from the queue, scale-to-zero comes from the function, and there is no polling loop to write. Swap the placeholder source bucket for the zip your build uploads.

Protected site5 nodes
NetworkCloud RunHTTPS load balancerDomainFirewall (Cloud Armor)

The public site behind Cloud Armor — a per-IP rate limit and preconfigured SQL-injection and XSS rules on the load balancer.

When to reach for it

The public site with Cloud Armor on the front: a per-IP rate limit with a ban, plus Google's preconfigured SQL-injection and XSS rules at their stable sensitivity. Use it for anything on the open internet that accepts input or gets scraped — the limit answers the crude problems (scripted abuse, credential stuffing) before they reach Cloud Run, and a banned IP gets 429s for the configured minutes, then starts over.

Static site3 nodes
Static siteDomainUptime check

A bucket served through the HTTPS load balancer with Cloud CDN at your domain, and an uptime check that emails you when it goes down.

When to reach for it

A marketing page, a docs site, a launch page: a bucket served through the HTTPS load balancer with Cloud CDN at your domain, and an uptime check that emails you when it stops answering. Use it when there is no server-side code at all — nothing runs, the CDN serves from cache for up to a day, and the uptime check watches the hostname your users actually type. Edit the zone, hostname and alert email to yours.

Azure

16 presets
Web service2 nodes
NetworkContainer App

A container on Azure Container Apps with its own network and HTTPS hostname.

When to reach for it

The smallest thing that deploys on Azure: a Container App on its own network, with HTTPS on its own hostname. The network is not decoration — it carries the resource group everything lives in and the Container Apps environment the app runs in, so a service without a network edge does not compile. Every other Azure preset is this one plus more nodes.

Web service + Postgres3 nodes
NetworkContainer AppPostgres

A container app and a private Postgres Flexible Server, with the connection details injected as environment variables and the password read from Key Vault.

When to reach for it

The standard CRUD app: a Container App with its own Postgres Flexible Server on a delegated subnet, private by construction. The service finds MAINDB_HOST, MAINDB_USER and the rest in its environment, and the password as a Key Vault reference its managed identity reads at start — the value never enters Terraform state. Burstable B1ms is a development size; raise the SKU for production and the server resizes in place.

Full web app5 nodes
NetworkContainer AppPostgresStorageKey Vault

A container app with Postgres, a private blob container, and a Key Vault for an API key — every connection wired.

When to reach for it

The production starter: database, a private blob container for uploads, and a Key Vault for the third-party API key every real app eventually has. The vault takes an iam edge as well as env, because on Azure the env edge only names the vault and the role assignment is what lets the app read from it. Set the secret's value in the portal or with az; the app reads it with its identity.

Web app + cache4 nodes
NetworkContainer AppPostgresRedis

A container app with private Postgres and Azure Cache for Redis — sessions and hot reads beside the database.

When to reach for it

Web-db plus Azure Cache for Redis on the Basic tier — the smallest cache Azure sells. Use it when sessions, rate counters or hot reads are hitting the database on every request: the service gets one CACHE_URL in the rediss:// form every client accepts, with the access key held as a Container Apps secret rather than a plain variable.

Worker + queue4 nodes
NetworkContainer AppWorkerQueue

A public API sending to a Service Bus queue and a worker consuming it — the queue keeps a traffic spike from becoming a database spike.

When to reach for it

The produce/consume pair: a public API sending to a Service Bus queue and a Worker draining it. Use it when requests trigger work that should not happen inside the request. The iam edge grants each identity send and receive on the queue, so neither holds a connection string, and the Worker has no ingress, no hostname and a fixed instance count — zero means paused.

Scheduled job3 nodes
NetworkJobPostgres

A Container Apps job on a cron schedule — a container that runs to completion, with its own timeout and retries.

When to reach for it

A Container Apps job on a cron schedule, with the database it reports on. Use it for the nightly report, the cleanup pass, the recurring import — anything that runs to completion with its own timeout and retries. On Azure the schedule is the job's own, so there is no scheduler node to draw; clear the schedule and the same job runs on demand with az containerapp job start.

Serverless app4 nodes
NetworkContainer AppCosmos DBStorage

A container app with a serverless Cosmos DB database and a private blob container — no database instance to size, pay per request.

When to reach for it

The no-database-instance shape: a Container App reading and writing a serverless Cosmos DB with its identity, and a blob container for files. Nothing to size, billed per request, idles free. The iam edge grants the built-in Data Contributor role on the account; the keys stay enabled so the exported key works on day one.

Web service + MySQL3 nodes
NetworkContainer AppMySQL

A container app and a private MySQL Flexible Server, on a network with the MySQL delegation switched on.

When to reach for it

Web-db with the engine swapped. The network carries the MySQL delegation and private zone — the one config difference from every other preset's network, because Postgres and MySQL cannot share a delegated subnet on Azure. Pick this when the app already speaks MySQL.

Image pipeline4 nodes
NetworkWorkerContainer AppRegistry

A private Container Registry, a builder worker that pushes to it, and a service that pulls from it with its managed identity.

When to reach for it

A private Container Registry and the workloads that use it: a builder Worker that pushes images and a service that pulls them. The iam edges grant AcrPull and attach the registry to each workload, so a private image pulls with the managed identity and no admin user or stored password.

Static site + API3 nodes
NetworkStatic siteContainer App

An Azure Static Web App for the built site and a container app for its API, on one network.

When to reach for it

A Static Web App for the built site beside a Container App for its API. The site is served from Azure's edge with HTTPS on its own hostname on the free tier; push content with the SWA CLI or a GitHub Actions workflow. Static Web Apps live in a handful of regions, so the node takes its own region when the network's is not one of them.

Protected public site6 nodes
NetworkContainer AppLoad BalancerDomain + TLSWeb FirewallUptime check

A Container App behind Premium Front Door, a managed-TLS domain, WAF and an uptime alert. Set the hostname and alert email before deploying.

Function and messaging3 nodes
NetworkFunctionPublish / Subscribe

A container-based Azure Function with a Service Bus topic, subscription, connection settings and identity grants. Supply a container containing your function code.

Static egress3 nodes
NetworkContainer AppStatic egress

A Container App with a NAT gateway and one static outbound IP for external allowlists.

Scheduled HTTP request3 nodes
NetworkContainer AppHTTP schedule

Call a public Container App endpoint every hour. Configure the request path and method for your application.

App with email3 nodes
NetworkContainer AppEmail

A Container App connected to Azure Communication Services and an Azure-managed sending domain. Send through the Azure Email SDK in your code.

Event streaming3 nodes
NetworkContainer AppEvent Stream

A Container App and a partitioned Event Hubs stream with a consumer group, connection settings and managed identity grants. Add your producer or consumer code.

Cloud credentials

Ackee holds no ambient cloud credentials. Nothing deploys until a project connects its own — and the credential is scoped to that project, not the account.

AWS
An access key, or a cross-account IAM role Ackee assumes — no long-lived key stored.
Google Cloud
Three options: a pasted service-account key; Sign in with Google, which stores a revocable refresh token instead of a key; or workload identity federation, where your project trusts Ackee as an OIDC issuer and no secret is stored at all. Whichever you pick, pre-deploy checks auto-enable the Google APIs your graph needs, and the Requirements tab lists the exact predefined roles the credential must hold. Firestore is the one node whose access grant writes project IAM policy, so it is the reason Project IAM Admin is among them. Deploys run in an isolated Cloud Build worker inside your own project — Ackee's shared process never executes your graph — which is why the Requirements tab also lists the roles that bootstrap that runner (Cloud Build Editor, Service Account Admin and User, Project IAM Admin) and the pre-deploy check enables the Cloud Build, IAM and Cloud Logging APIs alongside the ones your nodes need.
Azure
A service principal.

Use Ackee from an AI agent

MCP

Ackee is an MCP server. Claude Code, claude.ai, Cursor and any other client that speaks the Model Context Protocol can list your projects, read and edit graphs, plan, read the diff, approve applies, read logs and outputs — through the same rules the editor enforces: a verified email, your plan's allotment, your role on each project. Nothing an agent can do bypasses the plan → approve → apply path, and nothing it reads includes cloud credentials, compiled infrastructure code or raw plan output.

Interactive apps
Add https://api-production-0171.up.railway.app/v1/mcp as an MCP server. The app sends you to Ackee to sign in and approve it — no token to copy. In Claude Code, register it from a terminal, then run /mcp to sign in:
claude mcp add --transport http ackee https://api-production-0171.up.railway.app/v1/mcp
In claude.ai, add it as a custom connector by URL. Account → Connected apps → Connect an app has the steps for Claude, Claude Code, ChatGPT, Codex, Cursor, VS Code and more, and is where you disconnect one.
Scripts and CI
Create a personal API token in Account → API tokens and send it as a bearer:
claude mcp add --transport http ackee https://api-production-0171.up.railway.app/v1/mcp --header "Authorization: Bearer ack_pat_…"
A token acts as you on projects and deployments and is refused wherever the account itself changes — sessions, password, second factor, billing, cloud credentials. Revoke it from the same place; changing your password revokes every token and app at once.
What it costs
Every plan includes a monthly number of tool calls — 1,000 on Free, 10,000 on Starter, 50,000 on Pro, 250,000 on Business — reset on the 1st (UTC). Free stops there; a paid plan bills $1 per 1,000 calls beyond it and is never blocked. Deploys an agent starts count against the deploy allotment as usual. whoami reports where the month stands, and so does Billing.
What an agent does
list_projectsget_graph / save_graphplan wait_for_deployment → read the diff → approve_deployment. Destroying needs the project's exact name typed back, and still stops for approval. The full tool list is what the server reports to the client.

AWS nodes

hashicorp/aws ~> 6.19

Twenty-nine nodes: twenty-four that create, four that look up what already exists, and Raw resource — any resource the provider supports, wired by hand, for the services no bundled node covers yet. Compute runs on ECS Fargate, so unlike the other two clouds there is no platform-managed ingress: the Service node carries its own security group, and real traffic arrives through the Load Balancer, Domain and Certificate trio. Network, Postgres, Bucket and Secret each have an existing-resource sibling that reads a resource by ID instead of creating one, exporting the same values so edges wire into either kind unchanged — the way into an account that already runs infrastructure. In the console, cv opens an AWS node's resource on its console page — every node but Domain, which creates nothing of its own to open, and Raw resource, whose type isn't known until you pick one.

Networkaws/vpc@1
network

The network everything else lives in. One node gives you a VPC with DNS enabled, an internet gateway, and public subnets spread across availability zones.

Creates

A VPC, an internet gateway, one public route table, and azCount public subnets each with a route table association. Subnet CIDRs are carved automatically from the block you give it.

FieldTypeEditor default
namerequiredstringmain
cidrstring10.0.0.0/16
azCount2 / 3 / 42
Ports
  • outnetwork
Exports
vpcIdcidrsubnetIds
Worth knowing
  • Public subnets only — no NAT gateway, deliberately. NAT is roughly $32/month each and would be a nasty surprise on a first bill. Private subnets are a later milestone.
  • Every resource on this VPC gets a public IP by default; see the Service node for what that means.
Serviceaws/ecs-service@1
compute

A container on ECS Fargate. Draw an env edge from it to read a database's connection details, an iam edge to reach a bucket or secret, and a traffic edge into it from a Load Balancer for a stable address.

Creates

An ECS cluster, a CloudWatch log group (14-day retention), two IAM roles — the execution role, and a separate task role so app permissions from iam edges never widen what the ECS agent itself can do — a security group with the container port open inbound, a Fargate task definition, and the service itself with a public IP.

FieldTypeEditor default
namerequiredstringapi
imagerequiredstringpublic.ecr.aws/nginx/nginx:latest
cpu256 / 512 / 1024 / 2048 / 4096512
memorynumber1024
portnumber8080
desiredCountnumber1
envVarskey / value{}
Ports
  • innetwork
  • outenv
  • outiam
  • intraffic
Exports
logGroupsgIdtaskRoleNametaskRoleArnexecRoleNameclusterNameserviceNameportcontainerName
Worth knowing
  • Requires a network edge from a Network node — compile fails without one.
  • Unlike Cloud Run or Container Apps, a raw Fargate service has no platform-managed ingress at all: without the explicit ingress rule here, a public IP would be assigned but unreachable.
  • A traffic edge from a Load Balancer adds a stable DNS name, but the direct-port ingress rule remains — the load balancer doesn't replace it.
Postgresaws/rds-postgres@1
data

A managed Postgres instance on RDS, encrypted and never publicly reachable.

Creates

A security group, a DB subnet group, and the database instance — storage encrypted, not publicly accessible, daily backups retained.

FieldTypeEditor default
namerequiredstringmaindb
version15 / 16 / 1716
instanceClassdb.t4g.micro / db.t4g.small / db.t4g.medium / db.m7g.largedb.t4g.micro
storageGbnumber20
Ports
  • innetwork
  • inenv
Exports
hostportdbNameusernamesecretArnsgId
Worth knowing
  • The password is never in Terraform state: AWS generates and rotates it into Secrets Manager, and consumers get the secret's ARN instead of a raw value.
  • Not reachable from the public internet by design — it needs a network edge to the same Network node as anything that connects to it.
Bucketaws/s3-bucket@1
storage

A private S3 bucket. Grant a Service access with an iam edge; there is no public-bucket option in this node.

Creates

The bucket, a public-access block with all four flags on — ACLs and policies fully blocked — and a versioning resource when versioning is on.

FieldTypeEditor default
namerequiredstringassets
versioningbooleantrue
Ports
  • iniam
  • inenv
Exports
arnbucketregion
Worth knowing
  • Bucket names are globally unique across all of AWS, so the actual cloud name is your name plus a deterministic hash suffix — the same graph never produces a different bucket name, but it won't equal exactly what you typed.
  • No network port — buckets aren't VPC-scoped. Access goes through an iam edge from a Service.
Secretaws/secret@1
security

An empty Secrets Manager container. You set the value out of band; services read it at container start through an env edge.

Creates

An empty Secrets Manager secret — the container only, never a value.

FieldTypeEditor default
namerequiredstringapi-key
descriptionstring
Ports
  • inenv
Exports
arnsecretArnname
Worth knowing
  • The value is never set through Ackee or Terraform — writing it that way would put it in state in plaintext. Set it in the console or CLI; ECS resolves it when the container starts.
Network (existing)aws/vpc-ref@1
network

An existing VPC, looked up by ID instead of created. Draw the same network edge from it that you would from a Network node — a Service cannot tell the difference.

Creates

Nothing. A data lookup of the VPC by ID, plus — when subnetIds is left empty — a lookup of its public subnets, the ones with map-public-ip-on-launch set. That is the same shape a Network node exports, and what a Service and a Load Balancer assume they are given.

FieldTypeEditor default
vpcIdrequiredstring
subnetIdsstring
Ports
  • outnetwork
Exports
vpcIdcidrsubnetIds
Worth knowing
  • A lookup, never an adoption: a destroy leaves the VPC untouched, and Ackee never proposes changing it. Bringing an existing resource fully under management is a different, harder feature.
  • Auto-discovery finds public subnets but cannot pick one per availability zone. A Load Balancer refuses two subnets in the same zone at apply, so for a VPC with more than one public subnet per zone, list the ones to use in subnetIds.
  • The lookup runs at plan time with your deploy credential, so that credential needs permission to describe VPCs and subnets in the account.
  • In the console, cv finds this node by its VPC ID or its node id — it has no name field.
Postgres (existing)aws/rds-postgres-ref@1
data

An existing RDS instance, looked up by identifier. An env edge from a Service injects its host, port, database and username exactly as it would for a Postgres node.

Creates

Nothing. A data lookup of the instance by its identifier.

FieldTypeEditor default
identifierrequiredstring
secretArnstring
Ports
  • inenv
Exports
hostportdbNameusernameidentifiersecretArn (only when configured)
Worth knowing
  • No network port. The instance already lives in a VPC, and Ackee has no security group of its own to place there — so reachability from a Service is yours to arrange, not something an edge can draw.
  • secretArn is optional because a lookup has nothing to generate: the Postgres node's secret exists only because AWS writes the password into Secrets Manager on create. Paste an existing secret's ARN and env edges grant read on it; leave it empty and they inject the connection details without a password.
  • In the console, cv finds this node by its instance identifier or its node id.
Bucket (existing)aws/s3-bucket-ref@1
storage

An existing bucket, looked up by name. An iam edge from a Service grants the same least-privilege object access a Bucket node gets.

Creates

Nothing. A data lookup of the bucket by name.

FieldTypeEditor default
bucketrequiredstring
Ports
  • iniam
  • inenv
Exports
arnbucketregion
Worth knowing
  • The name is used exactly as typed — no hash suffix, because there is nothing to make unique. It has to be the bucket's real name.
  • Read-only lookup: a destroy never touches the bucket or its contents.
  • In the console, cv finds this node by its bucket name or its node id.
Secret (existing)aws/secret-ref@1
security

An existing Secrets Manager secret, by ARN. Services read it at container start through an env edge, the same as a Secret node.

Creates

Nothing. A data lookup of the secret by ARN.

FieldTypeEditor default
arnrequiredstring
Ports
  • inenv
Exports
arnsecretArnname
Worth knowing
  • ARN rather than name: the data source accepts either, but an ARN pins the account and region where a bare name could resolve ambiguously.
  • Its value is never read into Terraform — an env edge hands ECS the ARN to resolve at container start, exactly as with a Secret node.
  • In the console, cv finds this node by its full ARN or its node id.
Raw resourceaws/raw@1
compute

Any resource the AWS provider supports, wired by hand. The bundled nodes are opinions — an encrypted database whose password never enters state, a bucket that blocks public access. This is the other trade: exactly the one resource you name, none of those defaults, and the only way to reach the resource types no bundled node covers.

Creates

Exactly one resource, of the type you name, with the arguments you give it — nothing else, and no defaults added.

FieldTypeEditor default
resourcerequiredstring
attrsjson{}
blocksjson{}
exportskey / value{}
slotskey / value{}
iamActionsjson{}
Ports
  • innetwork
  • inenv
  • iniam
  • intraffic
Exports
whatever you declare
Worth knowing
  • You declare the wiring. "Exports" maps a name other nodes can read to an attribute of this resource (url → url); "Slots" maps a name an edge may fill to one of its arguments (vpcId → vpc_id). Until you declare them, this node connects to nothing.
  • Arguments and Nested blocks are separate fields because HCL treats them differently and the provider will not accept one for the other: a security group's ingress is a block, while its tags are an object-valued argument, and both look like a JSON object here. Blocks go in Nested blocks as [{ "type": "ingress", "attrs": {…} }], repeated for repeated blocks, and nest via their own "blocks" key.
  • An env edge into a raw node injects every export you declared, under its own name — a url export on a node labelled jobs becomes JOBS_URL. Bundled nodes use a fixed vocabulary instead, because host and bucket have to mean the same thing on every cloud.
  • An iam edge needs the actions spelled out, because no template exists for a resource type chosen at drawing time. Declare them in IAM actions and exactly those are granted, on exactly this resource's ARN.
  • No slot is ever required, because nothing here knows which of a given resource's arguments are mandatory — the provider's own validation is what catches a missing one, at plan time.
  • The permission preflight and the required-API check are keyed on resource type, so they cannot vouch for a raw resource. If your credentials cannot create it, the apply is where you find out.
Load Balanceraws/alb@1
network

An internet-facing Application Load Balancer — a stable DNS name in front of a Service, over HTTP out of the box and HTTPS with a Certificate node attached.

Creates

The load balancer, its own security group (ports 80 and 443 inbound from anywhere), a target group in IP mode for Fargate, and a port-80 listener. A traffic edge to a Certificate node adds a second, 443/HTTPS listener.

FieldTypeEditor default
namerequiredstringweb
healthCheckPathstring/
Ports
  • innetwork
  • outtraffic
Exports
dnsNamezoneIdalbArntargetGroupArnsgId
Worth knowing
  • Takes the same network edge a Service does, and its traffic edge has exactly three destinations: a Service (routes requests to it), a Domain (writes the DNS record), a Certificate (adds the HTTPS listener).
  • The target group's own port 80 is not what's routed — Fargate registers targets on whatever port the connected Service exports.
Domainaws/domain@1
network

Your domain name, pointed at a load balancer. Bring a domain already delegated to a Route 53 hosted zone in this account.

Creates

Nothing by itself — it exports values for a traffic edge to read. The edge from a Load Balancer is what creates the Route 53 alias record, so an unconnected Domain is a no-op, never a dangling record.

FieldTypeEditor default
hostedZoneIdrequiredstring
recordNamerequiredstring
Ports
  • intraffic
Exports
hostedZoneIdrecordName
Worth knowing
  • Ackee doesn't register or transfer domains — bring your own, the same way you bring your own container image.
Certificateaws/acm-certificate@1
security

A TLS certificate with fully automated DNS validation — no manual paste-this-record step, all inside one apply.

Creates

The ACM certificate, the Route 53 validation record the certificate itself computes, and the validation waiter — which is what makes apply actually wait until AWS confirms validation before the load balancer's HTTPS listener can reference it.

FieldTypeEditor default
domainNamerequiredstring
hostedZoneIdrequiredstring
Ports
  • intraffic
Exports
certificateArn
Worth knowing
  • One domain per certificate — no Subject Alternative Names.
  • Only meaningful with a traffic edge from a Load Balancer; that edge is what adds the 443 listener. Unconnected, the certificate is still created and validated, just unused.
Functionaws/lambda@1
compute

A function on Lambda, from a container image in ECR or a zip in S3, with its own log group and its own role. Draw an env edge to read a bucket's or secret's details, an iam edge to reach it, and a traffic edge from a Schedule to run it on a clock.

Creates

A CloudWatch log group with 14-day retention, created before the function and named in its logging_config so Lambda never creates an unmanaged one; an IAM role trusted by lambda.amazonaws.com with the basic execution policy attached; the function itself; and, with the public URL switch on, a function URL plus the resource policy that lets the public call it.

FieldTypeEditor default
namerequiredstringhandler
imagestring
s3Bucketstring
s3Keystring
runtimestringnodejs24.x
handlerstringindex.handler
memorynumber512
timeoutnumber30
publicUrlbooleanfalse
envVarskey / value{}
Ports
  • outenv
  • outiam
  • intraffic
Exports
functionNamefunctionArninvokeArnlogGrouptaskRoleNameexecRoleNameurl (only with a public URL)
Worth knowing
  • One code source: either a container image (ECR, same account) or a zip in S3 with a runtime and handler. Both is an error, neither is an error.
  • No network port, on purpose. The Network node creates public subnets only, and a function attached to one has no route to the internet or to any AWS API. So a function stays outside the VPC, and an env edge to a Postgres node is refused at compile time with the reason — a function reaches buckets, secrets and queues, not a private database.
  • Secrets arrive as an ARN: Lambda has no start-time secrets channel, so an env edge to a Secret injects NAME_SECRET_ARN and grants GetSecretValue on it. Read it with the SDK; the value never enters the plan or state.
  • The env slot is a map, not a list — Lambda's environment.variables is keyed by name — and env edges merge into it. A hand-typed variable with the same name as an injected one is overwritten by the edge.
  • The role wears both hats: iam edges attach application grants to it, and the env edge's secret grant does too. On a Service those are two roles.
  • In the console, cv opens the function's page by its name.
Scheduleaws/schedule@1
network

An EventBridge Scheduler schedule — a clock that invokes a Function on a rate, a cron, or once at a time, authenticated as its own role.

Creates

An IAM role trusted by scheduler.amazonaws.com, and the schedule in the default group with an exact (non-flexible) time window and three retries. The traffic edge fills the target and grants lambda:InvokeFunction on exactly that function.

FieldTypeEditor default
namerequiredstringhourly
schedulestringrate(1 hour)
timeZonestringEtc/UTC
inputstring
Ports
  • outtraffic
Exports
scheduleNamescheduleroleNameroleArn
Worth knowing
  • Only meaningful with a traffic edge to a Function; without one the schedule has no target and compile says so.
  • EventBridge's three forms only: rate(1 hour), cron(0 3 * * ? *) — six fields, with ? for one of day-of-month or day-of-week — or at(2026-12-31T23:59:00). A bare five-field cron is refused at compile with the fix in the message.
  • Retries are capped at three rather than the provider's default of 185: one bad deploy should not become a day of the same failure.
  • Scheduler invokes through its own role, so no aws_lambda_permission is added on the function side — nothing names a principal broader than this one schedule.
Queueaws/sqs@1
data

An SQS queue with its dead-letter queue. A Service reads it through env and iam edges; a traffic edge to a Function makes the function its consumer, with Lambda polling the queue.

Creates

The dead-letter queue (14-day retention, longer than the main queue's), the queue itself with a redrive policy pointing at it after maxReceiveCount receives, and the allow policy that lets this queue and no other redrive into the dead-letter queue. Both encrypted with SQS-managed keys.

FieldTypeEditor default
namerequiredstringjobs
visibilityTimeoutnumber30
retentionDaysnumber4
maxReceiveCountnumber5
fifobooleanfalse
Ports
  • inenv
  • iniam
  • outtraffic
Exports
urlarnqueuequeueNamedlqUrldlqArn
Worth knowing
  • An env edge hands a worker NAME_URL (what every SDK call takes) and NAME_QUEUE; an iam edge grants send, receive, delete, change-visibility and the attribute reads — never sqs:*, which includes deleting the queue.
  • A traffic edge to a Function creates an event source mapping: Lambda polls the queue and invokes the function per batch of ten, with partial-batch failures reported so one bad message does not fail the nine beside it. The function's role gets the read grants the poll needs. Set the visibility timeout to at least the function's timeout — six times it is AWS's own advice.
  • FIFO adds the .fifo suffix to both queue names and turns on content-based deduplication.
  • Filed under data, beside the GCP Queue: both hold work between a producer and a consumer.
  • In the console, cv opens the queue by its URL.
Firewall (WAF)aws/waf@1
security

A WAF web ACL in front of the Load Balancer: a per-IP rate limit and AWS's managed rule groups, attached by a traffic edge from the Load Balancer.

Creates

One regional web ACL with a default allow: a rate-based rule that blocks an IP over the limit per five minutes, and AWS's Common and Known-Bad-Inputs managed rule groups with their own actions in force. The traffic edge from the Load Balancer emits the association naming both.

FieldTypeEditor default
namerequiredstringedge
rateLimitnumber2000
managedRulesbooleantrue
Ports
  • intraffic
Exports
arnaclNameidentifier
Worth knowing
  • Only meaningful with a traffic edge from a Load Balancer (source: the load balancer; target: this node) — the same direction as the Certificate edge. Nothing on either node changes; the edge adds one association.
  • The five-minute window is WAF's own and not configurable; the limit is per source IP. Zero turns the rate rule off.
  • The managed groups are not overridden to count, so their per-rule block actions apply. Turn them off if a legitimate client trips the common set — Log4j-style probes and the like are what they block.
  • CloudWatch metrics and sampled requests are on for the ACL and every rule, so a block can be traced to the rule that made it.
Static siteaws/static-site@1
storage

A static site: a private bucket behind CloudFront, reached only through the distribution. A Function or Service uploads the site through an iam edge and reads the bucket name and URL through an env edge.

Creates

The bucket with public access blocked on all four counts, an origin access control that signs every CloudFront request, the distribution (HTTPS redirect, compression, AWS's CachingOptimized policy, 403 and 404 rewritten to the error document with a 200 for client-side routing), and a bucket policy granting s3:GetObject to the CloudFront service principal for this one distribution only.

FieldTypeEditor default
namerequiredstringwww
indexDocumentstringindex.html
errorDocumentstringindex.html
priceClassPriceClass_100 / PriceClass_200 / PriceClass_AllPriceClass_100
Ports
  • iniam
  • inenv
Exports
arnbucketregionurlidentifier
Worth knowing
  • Served at the distribution's own cloudfront.net name. A custom domain on CloudFront needs a certificate issued in us-east-1 whatever region the environment deploys to — a second provider configuration the compiler does not yet emit — so there is no traffic edge to a Domain here. Put a CNAME to the url in your DNS by hand if you need one, knowing the certificate will not match it.
  • The bucket policy is written after the distribution, because its condition names the distribution's ARN; and after the public access block, because S3 rejects a policy as public for the instant the block is missing.
  • Origin access identities, the older mechanism, are not used. OAC is what AWS recommends and the only one that supports SSE-KMS buckets.
  • The error document is served with a 200 for both 403 and 404 — a private bucket answers 403 for a missing key — which is what a single-page app's router needs. For a site with real 404 pages, point errorDocument at one and accept the 200.
  • In the console, cv opens the distribution by its id.
Email domainaws/ses@1
network

A sending domain on SES, with every DNS record that makes mail from it deliverable written into your Route 53 zone. A Function or Service sends through an iam edge.

Creates

The SESv2 domain identity with 2048-bit Easy DKIM, three DKIM CNAME records off the tokens SES issues, a custom MAIL FROM subdomain with its MX and SPF records, and a DMARC record with p=none — all in one apply, the way the Certificate node writes its validation record.

FieldTypeEditor default
domainrequiredstring
hostedZoneIdrequiredstring
mailFromSubdomainstringmail
dmarcbooleantrue
Ports
  • iniam
Exports
arndomainmailFromidentifier
Worth knowing
  • The iam edge grants SendEmail and SendRawEmail on this identity, nothing wider — never ses:*, which manages every identity in the account.
  • A new AWS account starts in the SES sandbox: mail goes only to verified addresses until AWS grants production access, which is a support request Ackee cannot make for you.
  • The MAIL FROM subdomain is what DMARC alignment checks against; without it every message carries an amazonses.com envelope sender and fails SPF alignment. The MX record points at feedback-smtp in the environment's region.
  • DMARC is p=none: reports, never rejection. Tighten to quarantine with the reports in hand. Turn the switch off if the zone already carries a DMARC record.
  • Filed under network beside Domain: it is a domain and its records. AWS_NODES.md flagged SES as fitting no category cleanly; this is the least bad answer.
  • In the console, cv opens the identity by its domain.
Redisaws/redis@1
data

Private ElastiCache Redis with encrypted storage and TLS. Connect a workload with an env edge to receive its rediss URL and allow that workload through the security group.

Creates

Replication group, subnet group and security group.

FieldTypeEditor default
namerequiredstringcache
nodeTypestringcache.t4g.micro
nodes1 / 2 / 3 / 4 / 5 / 61
Ports
  • innetwork
  • inenv
Exports
hostporturlidentifier
Worth knowing
  • Single node by default; two or more enable Multi-AZ failover.
  • TLS is required. Access uses security groups; AUTH/RBAC is not configured.
  • Use the same Network as consuming workloads. Valkey requires AWS provider 5.73 or newer.
Valkeyaws/valkey@1
data

Private ElastiCache Valkey with encrypted storage and TLS. Connect a workload with an env edge to receive its rediss URL and allow that workload through the security group.

Creates

Replication group, subnet group and security group.

FieldTypeEditor default
namerequiredstringcache
nodeTypestringcache.t4g.micro
nodes1 / 2 / 3 / 4 / 5 / 61
Ports
  • innetwork
  • inenv
Exports
hostporturlidentifier
Worth knowing
  • Single node by default; two or more enable Multi-AZ failover.
  • TLS is required. Access uses security groups; AUTH/RBAC is not configured.
  • Use the same Network as consuming workloads. Valkey requires AWS provider 5.73 or newer.
Queueaws/queue@1
data

An SNS topic delivering raw messages to an encrypted SQS pull queue. Env exports the topic ARN and queue URL; IAM grants publish and consume access.

Creates

SNS topic, SQS queue, topic subscription and source-scoped delivery policy.

FieldTypeEditor default
namerequiredstringjobs
visibilityTimeoutnumber30
retentionDaysnumber4
Ports
  • inenv
  • iniam
Exports
topicurlregion
Worth knowing
  • Standard delivery: consumers must handle duplicate messages.
  • This is one subscription, not a configurable fan-out topology. No dead-letter queue yet.
  • An IAM edge grants both producer and consumer operations; separate role modes are a follow-up.
DynamoDBaws/dynamodb@1
data

An on-demand key/value and document table with encryption and point-in-time recovery. Its API and query model differ from Firestore.

Creates

DynamoDB table.

FieldTypeEditor default
namerequiredstringdocuments
partitionKeystringid
sortKeystring
pointInTimeRecoverybooleantrue
Ports
  • inenv
  • iniam
Exports
tableregion
Worth knowing
  • Keys are strings. Secondary indexes, streams and global tables require raw resources today.
  • An IAM edge grants item operations on this table, not table administration.
Image Registryaws/ecr@1
storage

A private ECR image repository with scan-on-push and encryption. An IAM edge lets a workload push and pull images.

Creates

ECR repository.

FieldTypeEditor default
namerequiredstringimages
immutableTagsbooleantrue
Ports
  • inenv
  • iniam
Exports
repositoryregistryregion
Worth knowing
  • Does not build or upload an image, or change a workload’s image automatically.
  • The repository must be emptied before destroy; force deletion is off.
  • ECR token authorization uses Resource *, while image operations are scoped to this repository.
MySQLaws/rds-mysql@1
data

Private RDS MySQL with encrypted storage and an AWS-managed password in Secrets Manager. Env edges supply connection values and runtime secret access.

Creates

RDS instance, DB subnet group and security group.

FieldTypeEditor default
namerequiredstringmysql
versionstring8.0
instanceClassstringdb.t4g.micro
storageGbnumber20
Ports
  • innetwork
  • inenv
Exports
hostportdbNameusernamesecretArnidentifier
Worth knowing
  • Network and consuming workloads must share a VPC.
  • One day of backups; destroy skips a final snapshot, matching the existing RDS Postgres node.
Aurora Postgresaws/aurora-postgres@1
data

An encrypted Aurora PostgreSQL cluster with an AWS-managed master password. A separate node from RDS Postgres, so existing databases never change engine implicitly.

Creates

Aurora cluster, instances, subnet group and security group.

FieldTypeEditor default
namerequiredstringclusterdb
instanceClassstringdb.r6g.large
instances1 / 2 / 31
Ports
  • innetwork
  • inenv
Exports
hostportdbNameusernamesecretArnidentifier
Worth knowing
  • Provisioned instances incur continuous cost; one instance is not compute failover redundancy.
  • AWS selects the initial engine version available in the region. Seven days of backups.
  • Destroy creates a final snapshot; a retained snapshot with that name must be renamed or removed before a later destroy.
Workeraws/worker@1
compute

An ECS Fargate service for continuous background processing, with no inbound port.

Creates

ECS cluster, task definition, execution/task roles, logs and outbound-only security group, plus an ECS service.

FieldTypeEditor default
namerequiredstringconsumer
imagerequiredstringpublic.ecr.aws/docker/library/alpine:3.24
cpu256 / 512 / 1024 / 2048 / 4096256
memorynumber512
commandjson["sh","-c","while true; do sleep 60; done"]
envVarskey / value{}
desiredCountnumber1
Ports
  • innetwork
  • outenv
  • outiam
Exports
clusterNametaskDefinitionArnlogGroupserviceName
Worth knowing
  • No HTTP ingress or load-balancer port. Network, env and IAM edges work as on Service.
  • Public IPs support outbound image pulls on the current public-subnet Network; security groups allow no inbound traffic.
  • Zero instances pauses the worker.
Jobaws/job@1
compute

An ECS Fargate task definition for work that exits. Connect Cron → Job to run it on a schedule.

Creates

ECS cluster, task definition, execution/task roles, logs and outbound-only security group.

FieldTypeEditor default
namerequiredstringbatch
imagerequiredstringpublic.ecr.aws/docker/library/alpine:3.24
cpu256 / 512 / 1024 / 2048 / 4096256
memorynumber512
commandjson["sh","-c","echo job completed"]
envVarskey / value{}
Ports
  • innetwork
  • outenv
  • outiam
  • intraffic
Exports
clusterNametaskDefinitionArnlogGroup
Worth knowing
  • No HTTP ingress or load-balancer port. Network, env and IAM edges work as on Service.
  • Public IPs support outbound image pulls on the current public-subnet Network; security groups allow no inbound traffic.
  • Creating the node registers a task; it does not execute it. Run manually through ECS or connect a Cron.
  • Connect both Job and Cron to the same Network. Scheduler retries do not retry a task that starts successfully and later fails.
Cronaws/cron@1
network

EventBridge Scheduler starts an ECS Job using its own scoped execution role. Connect Network → Cron and Cron → Job.

Creates

Schedule group, schedule, IAM role and a task-specific RunTask/PassRole policy from the traffic edge.

FieldTypeEditor default
namerequiredstringnightly
schedulestringcron(0 3 * * ? *)
timeZonestringUTC
enabledbooleantrue
Ports
  • innetwork
  • outtraffic
Exports
scheduleNamescheduleGroup
Worth knowing
  • AWS six-field cron(...) or rate(...), not Google’s five-field cron syntax.
  • Only Job targets are supported; it does not call a Service HTTP endpoint. Both Job and Cron must use the same VPC.
  • Runs begin after apply when enabled. Delivery retries are disabled; task failures need application-level handling.

Google Cloud nodes

hashicorp/google ~> 8.1

Twenty-eight nodes: twenty-three that create, four that look up what already exists, and Raw resource — any of the resource types the google provider supports, wired by hand, for the services no bundled node covers yet. Deliberately not a translation of the AWS set. Cloud Run has no security groups and no task definition — it is a container URL with an env block — so where the shapes genuinely differ, the node follows Google's model instead of pretending it is EC2. There is no per-service security group anywhere; the Network node exists to give private backends a VPC, not to gate traffic. Network, Postgres, Bucket and Secret each have an existing-resource sibling that reads a resource by name instead of creating one, exporting the same values so edges wire into either kind unchanged. Import from Google Cloud, on the toolbar, reads what already runs in the project and places the references for you; a plan of references alone changes nothing and does not count against the free allowance. In the console, cv opens a GCP node's resource on its Google Cloud console page — every node but Domain, which creates nothing of its own to open, Cron, whose console has no per-job page, Static egress, whose NAT page has no documented address, and Raw resource, whose type isn't known until you pick one.

Networkgcp/vpc@1
network

A VPC with one private subnet and the private-services peering that Cloud SQL and Redis private IP require. Cloud Run reaches it through a direct interface — no connector VMs.

Creates

The VPC (no auto-created subnets — Google would otherwise put one in every region), one subnet in the environment's region, a reserved /16 for private services access, and the service-networking peering itself.

FieldTypeEditor default
namerequiredstringmain
cidrstring10.0.0.0/20
valkeyAccessbooleanfalse
Ports
  • outnetwork
Exports
networkIdnetworkNamesubnetIdpeeringpeeringNetwork
Worth knowing
  • The CIDR is validated at compile time: it must parse and must not overlap the pinned private-services range — Terraform would otherwise accept it, and Google would refuse it minutes into the apply.
  • No Serverless VPC Access connector, deliberately: it cost two always-on VMs per network — more than the database beside it. Direct VPC egress replaced it.
  • The peeringNetwork export is an ordering trick: the same network id, read through the peering, so databases are created only after private services access exists.
Cloud Rungcp/cloud-run@1
compute

A container URL with an env block. Public by default; switched off, only callers Google can authenticate reach it — like its own Cron.

Creates

A dedicated service account — so iam edges grant to this service, never the project's shared default account — and the Cloud Run service: capped instances, request-billed CPU, and a direct VPC interface for private backends.

FieldTypeEditor default
namerequiredstringapi
imagerequiredstringus-docker.pkg.dev/cloudrun/container/hello
cpu1 / 2 / 41
memorystring512Mi
portnumber8080
maxInstancesnumber3
publicbooleantrue
envVarskey / value{}
Ports
  • innetwork
  • outenv
  • outiam
  • intraffic
Exports
serviceAccountserviceAccountMemberurlserviceNameport
Worth knowing
  • Requires a network edge from a Network node; the direct VPC interface needs both halves and compile fails without it. Only private ranges ride the VPC — everything else goes out Google's front door as normal.
  • public switches off the invoker IAM check rather than binding allUsers — it needs no extra permissions on the deployer and works under the domain-restricted-sharing org policy, where an allUsers binding hard-fails.
  • CPU is request-billed (cpu_idle) — stated explicitly, because stating resource limits at all would otherwise flip Cloud Run's default to always-on CPU.
Cloud SQLgcp/cloud-sql-postgres@1
data

A managed Postgres instance on Cloud SQL, private IP only.

Creates

The Cloud SQL instance, a database, a generated 32-character password, the master user, and a Secret Manager secret holding that password. Backups and point-in-time recovery are explicitly on.

FieldTypeEditor default
namerequiredstringmaindb
versionPOSTGRES_15 / POSTGRES_16 / POSTGRES_17POSTGRES_16
tierdb-f1-micro / db-g1-small / db-custom-1-3840db-f1-micro
editionENTERPRISE / ENTERPRISE_PLUSENTERPRISE
Ports
  • innetwork
  • inenv
Exports
hostportdbNameusernamesecretIdsecretRefsecretVersionidentifier
Worth knowing
  • Google has no managed master password (unlike RDS), so one is generated — ephemerally. It reaches the instance and the Secret Manager secret through write-only attributes, so it never enters Terraform state or the plan. Consumers get it through an env edge as a reference resolved at container start, never as a literal.
  • The edition/tier pairing is enforced at compile time — left to its own defaults, Postgres 16 picks an edition that rejects the shared-core tiers, and the apply dies minutes in. The compiler refuses first.
  • Private IP only — needs a network edge to the same Network node as anything that connects to it.
  • Backups and PITR are on because API-created instances default them off (console-created ones don't) — a database with no backups because of that divergence is not a default anyone chose.
Bucketgcp/gcs-bucket@1
storage

A private GCS bucket with uniform access and public-access prevention both enforced.

Creates

The bucket — uniform bucket-level access on, public access prevention enforced, versioning when on.

FieldTypeEditor default
namerequiredstringassets
versioningbooleantrue
forceDestroybooleanfalse
Ports
  • iniam
  • inenv
Exports
bucketbucketNameurlregion
Worth knowing
  • GCS bucket names are globally unique, same as S3 — the actual name is yours plus a deterministic hash suffix.
  • Uniform access and public-access prevention are different controls, routinely confused, and only one of them keeps a bucket off the open internet — so both are set.
  • forceDestroy is off by default so destroy never takes objects with it by surprise — but it exists, because without it Ackee's own destroy could never remove a bucket that has been written to.
  • An iam edge grants roles/storage.objectUser — read and write without bucket admin, so the app can't delete the bucket itself.
Secretgcp/secret@1
security

An empty Secret Manager container. You set the value out of band; Cloud Run resolves it at container start.

Creates

An empty Secret Manager secret — the container only, never a value.

FieldTypeEditor default
namerequiredstringapi-key
descriptionstring
Ports
  • inenv
  • iniam
Exports
secretIdsecretRefname
Worth knowing
  • The value is never set through Ackee or Terraform — that would put it in state in plaintext.
  • An env edge from a service injects it properly: Cloud Run has no separate secrets channel, so the secret becomes an env var sourced from Secret Manager at version latest, and the edge adds the accessor grant for the service's account.
Network (existing)gcp/vpc-ref@1
network

An existing VPC and one of its subnets, looked up by name instead of created. Draw the same network edge from it that you would from a Network node.

Creates

Nothing. A data lookup of the network by name, and of the subnet by name in this project's region.

FieldTypeEditor default
networkrequiredstring
subnetrequiredstring
hasPrivateServicesAccessbooleanfalse
Ports
  • outnetwork
Exports
networkIdnetworkNamesubnetIdpeeringNetwork (only when the peering switch is on)
Worth knowing
  • A lookup, never an adoption: a destroy leaves the network untouched, and Ackee never proposes changing it.
  • Cloud SQL and other private-IP databases need Private Services Access peering on the network, and nothing at plan time can check for it. Turn the switch on only if the VPC already has that peering. With it off, wiring a managed database to this network fails at compile time with the fix named, rather than minutes into an apply.
  • The subnet has to be in this project's region: Cloud Run's direct VPC egress attaches there, and a reference cannot guess which one.
  • In the console, cv finds this node by its network name or its node id — it has no name field.
Postgres (existing)gcp/cloud-sql-postgres-ref@1
data

An existing Cloud SQL Postgres instance, looked up by name. An env edge from a service injects its host, port, database and user exactly as it would for a Cloud SQL node.

Creates

Nothing. A data lookup of the instance, plus a lookup of the password secret when one is named.

FieldTypeEditor default
instancerequiredstring
dbNamestringpostgres
usernamestringpostgres
passwordSecretIdstring
usePublicIpbooleanfalse
Ports
  • inenv
Exports
hostportdbNameusernameconnectionNameidentifiersecretId and secretRef (only when passwordSecretId is set)
Worth knowing
  • No network port: the instance already lives in a VPC, and a lookup has nothing to place anywhere.
  • Database and user default to postgres, because a lookup cannot read them from the instance. Change them if the app connects as something else.
  • A Cloud SQL node generates its password into Secret Manager; a lookup has nothing to generate. Name an existing secret and env edges inject it at container start and grant read on it. Leave it empty and they inject the connection details without a password.
  • host is the private IP by default. Turn on the public-IP switch for an instance with no private address, or the injected host is empty. A public address is reachable only from what the instance authorises: Ackee does not configure authorized networks or the Cloud SQL socket, so with the switch on, connectivity is yours to arrange on the instance.
  • connectionName is exported for apps that connect through the Cloud SQL Auth Proxy or connector; nothing in Ackee reads it yet.
  • In the console, cv finds this node by its instance name or its node id — it has no name field.
Bucket (existing)gcp/gcs-bucket-ref@1
storage

An existing bucket, looked up by name. An iam edge from a service grants the same object access a Bucket node gets.

Creates

Nothing. A data lookup of the bucket by name.

FieldTypeEditor default
bucketrequiredstring
Ports
  • inenv
  • iniam
Exports
bucketbucketNameregionidentifier
Worth knowing
  • The name is used exactly as typed, with no hash suffix; it has to be the bucket's real name.
  • Read-only lookup: a destroy never touches the bucket or its contents.
  • region is the bucket's location in lower case, which for a multi-region bucket is a region group such as us rather than a region.
  • In the console, cv finds this node by its bucket name or its node id.
Secret (existing)gcp/secret-ref@1
security

An existing Secret Manager secret, by id. Cloud Run resolves it at container start through an env edge, the same as a Secret node.

Creates

Nothing. A data lookup of the secret by id.

FieldTypeEditor default
secretIdrequiredstring
Ports
  • inenv
  • iniam
Exports
secretIdsecretRefnameidentifier
Worth knowing
  • Its value is never read into Terraform: an env edge sources it at version latest at container start, and adds the accessor grant for the service's account.
  • Read-only lookup: a destroy never touches the secret or its versions.
  • In the console, cv finds this node by its secret id or its node id.
Redisgcp/redis@1
data

A Memorystore Redis instance on the VPC's private peering. The env edge hands a service one CACHE_URL — no string assembly at boot.

Creates

One Memorystore instance, Redis 7.2 pinned, connected over private services access.

FieldTypeEditor default
namerequiredstringcache
tierBASIC / STANDARD_HABASIC
memoryGbnumber1
Ports
  • innetwork
  • inenv
Exports
hostporturlidentifier
Worth knowing
  • Deliberately Redis, not the newer Valkey: Valkey connects over Private Service Connect — a second networking mechanism beside the peering the Network node already builds — and clients can't tell the engines apart at 7.2. When Valkey earns its keep it will be a node beside this one, never a mutation of it.
  • The Redis version is pinned so a future provider default bump can't replace the instance under a graph that never asked for it.
Queuegcp/pubsub@1
data

A Pub/Sub topic and its worker pull subscription, shipped together — one producer, one worker, one drawn line each.

Creates

The topic and one pull subscription that never expires. Exports are full resource paths, which every client library accepts verbatim.

FieldTypeEditor default
namerequiredstringjobs
ackDeadlinenumber20
Ports
  • inenv
  • iniam
  • outtraffic
Exports
topicsubscription
Worth knowing
  • The subscription ships with the topic because a topic nobody consumes is a black hole — Pub/Sub drops messages with no subscription attached.
  • One iam edge grants both halves: publisher on the topic and subscriber on the subscription. Producer and worker draw the same edge and each uses what its role needs at runtime.
  • The subscription never expires — by default Pub/Sub deletes one after 31 idle days, and a worker scaled to zero for a month coming back to find its queue gone is not a default anyone drew on a canvas.
  • A traffic edge to a Function makes each message invoke it through Eventarc, beside the pull subscription — both consumers get every message.
Crongcp/cron@1
network

A Cloud Scheduler job that calls a Cloud Run service, or starts a Job, on a schedule — authenticated as its own identity.

Creates

Its own service account — so the invoker grant names this one job, never a shared identity every scheduled task would ride — and the Scheduler job with a 3-minute attempt deadline. The traffic edge appends the auth block, because a service and a Job want different tokens.

FieldTypeEditor default
namerequiredstringnightly
schedulestring0 3 * * *
timeZonestringEtc/UTC
pathstring/
Ports
  • outtraffic
Exports
serviceAccountserviceAccountMemberrequestPathschedule
Worth knowing
  • Only meaningful with a traffic edge to a Cloud Run service or a Job. To a service, the edge fills the target URL, adds an OIDC token pinned to the service's own URL, and grants run.invoker — so a private service still accepts its own cron and nobody else's.
  • To a Job the edge targets the Cloud Run Admin API's run endpoint with an OAuth token instead, since a Google API wants an access token, not an identity token. The request path is ignored there.
  • The 3-minute request deadline is Scheduler's own HTTP default, stated out loud so a slow nightly task failing at exactly 180 seconds is explicable from the plan alone.
HTTPS load balancergcp/https-lb@1
network

A global HTTPS front door for a Cloud Run service: static IP, Google-managed certificate, and an HTTP-to-HTTPS redirect.

Creates

Ten resources — the honest cost of Google's supported pattern for a Cloud Run custom domain: a global static IP, a serverless NEG, a backend service, a URL map, a managed SSL certificate, the HTTPS proxy and 443 forwarding rule, and a redirect chain whose sole job is to send port-80 visits to 443.

FieldTypeEditor default
namerequiredstringfront
Ports
  • outtraffic
Exports
iplbName
Worth knowing
  • This node exists because Cloud Run's own domain mapping is still a preview feature Google documents as not production-ready — the load balancer is their supported answer.
  • Both traffic edges are required — one to a Cloud Run service (the backend), one to a Domain (the certificate's hostname). A load balancer missing either is not a thing Google will create cleanly, so it fails at compile instead of mid-apply.
  • Until DNS points the hostname at the exported IP, the LB answers with a placeholder certificate — expected, and it self-heals once Google validates by serving.
Domaingcp/domain@1
network

Your domain name, pointed at the HTTPS load balancer. Bring a Cloud DNS zone that already exists in the target project.

Creates

Nothing by itself — the traffic edge from the load balancer writes the A record and adds this hostname to the LB's managed certificate. Unconnected, it is a value, never a dangling DNS entry.

FieldTypeEditor default
zoneNamerequiredstring
hostnamerequiredstring
Ports
  • intraffic
Exports
zoneNamehostnamefqdn
Worth knowing
  • Ackee points existing domains at deployments; it does not register or manage them.
  • Both spellings of the hostname are exported deliberately: DNS records require the trailing dot and certificate domains reject it.
Workergcp/worker@1
compute

A Cloud Run worker pool: instances that stay up and pull work from a queue. No URL, no request-driven scaling — the shape Google built for a Pub/Sub consumer.

Creates

A dedicated service account and the worker pool itself: a fixed instance count, a direct VPC interface for private backends, and the container with its CPU and memory limits.

FieldTypeEditor default
namerequiredstringworker
imagerequiredstringus-docker.pkg.dev/cloudrun/container/hello
cpu1 / 2 / 41
memorystring512Mi
instancesnumber1
envVarskey / value{}
Ports
  • innetwork
  • outenv
  • outiam
Exports
serviceAccountserviceAccountMemberpoolName
Worth knowing
  • Requires a network edge from a Network node, like Cloud Run — a worker almost always also talks to a private database or cache.
  • There is no port and no public switch: nothing listens. A cron or a load balancer cannot point at it, which is why it has no traffic port.
  • Billed per instance for as long as it runs, roughly 40% below a service doing the same background work — but a paused pool is instances set to zero, not scale-to-zero.
Jobgcp/job@1
compute

A Cloud Run job: a container that runs to completion, with its own timeout, retries and task count. The second thing a Cron can point at.

Creates

A dedicated service account and the job with an execution template — task count, then per-task timeout, retries, a direct VPC interface and the container.

FieldTypeEditor default
namerequiredstringtasks
imagerequiredstringus-docker.pkg.dev/cloudrun/container/hello
cpu1 / 2 / 41
memorystring512Mi
timeoutstring600s
retriesnumber3
taskCountnumber1
envVarskey / value{}
Ports
  • innetwork
  • outenv
  • outiam
  • intraffic
Exports
serviceAccountserviceAccountMemberjobNamerunUrl
Worth knowing
  • Starting a job is a call to the Cloud Run Admin API at its run endpoint, not an HTTP request to the job. The Cron's traffic edge knows: it sends an OAuth token there rather than the OIDC token a service takes, and grants run.invoker on the job.
  • The Cron's request path is ignored for a job — there is nothing to append to an API method.
  • Requires a network edge from a Network node, the same as every other container here.
Firestoregcp/firestore@1
data

A named Firestore database — the catalogue's serverless option, with no VPC and no instance to size. Enterprise edition speaks the MongoDB wire protocol.

Creates

One Firestore Native database with seven days of point-in-time recovery, delete protection off and a deletion policy that actually deletes, so Ackee's own destroy can remove it.

FieldTypeEditor default
namerequiredstringdocs
editionSTANDARD / ENTERPRISESTANDARD
locationstring
Ports
  • inenv
  • iniam
Exports
dbNameprojectdatabasePathuri
Worth knowing
  • Always a named database, never (default): the default database has special status in several client libraries and may already be in use by something else in the project.
  • Location is immutable — changing it destroys and recreates the database, data included. It defaults to the project region; multi-regions like nam5 or eur3 are valid too.
  • The iam edge grants datastore.user at project level, scoped by an IAM condition to this one database's resource name. That binding is the only thing in the catalogue that writes project IAM policy, so the deployer needs the Project IAM Admin role for it.
  • The uri export exists only on Enterprise edition: Google's MongoDB-compatible connection string, authenticating over OIDC as the workload's own service account — no password anywhere.
AlloyDBgcp/alloydb@1
data

An AlloyDB Postgres cluster with a primary instance — for workloads that outgrow Cloud SQL, on the same private peering.

Creates

A cluster and its primary instance on private services access, a generated master password held in a Secret Manager secret, and the initial user.

FieldTypeEditor default
namerequiredstringmaindb
versionstringPOSTGRES_16
cpuCount2 / 4 / 8 / 16 / 32 / 642
availabilityZONAL / REGIONALZONAL
trialbooleanfalse
Ports
  • innetwork
  • inenv
Exports
hostportdbNameusernamesecretIdsecretRefsecretVersionclusterNameidentifier
Worth knowing
  • The password lands in Terraform state. At the pinned provider version AlloyDB has no write-only password argument, so the ephemeral path Cloud SQL uses is not available — this node has the same standing as Azure's Postgres, and the same reason state is treated as secret material. The container still gets it as a Secret Manager reference, never a literal.
  • AlloyDB ships every cluster with a postgres database and has no database resource of its own, so dbName is postgres — the app creates its own from there.
  • Roughly a 39% premium over Cloud SQL Enterprise Plus. The trial switch makes it a 30-day free cluster, which is the cheap way to find out whether the engine earns it.
  • ZONAL by default. REGIONAL doubles the instance for high availability — a production decision, not a starting point.
Valkeygcp/valkey@1
data

Memorystore for Valkey — Google's recommended cache engine for new work — over Private Service Connect. The env edge hands a service one CACHE_URL, exactly like Redis.

Creates

One Memorystore instance: a single shard with cluster mode off, so every ordinary Redis client works against it, reached through a PSC endpoint the instance creates in the VPC's subnet.

FieldTypeEditor default
namerequiredstringcache
nodeTypeSHARED_CORE_NANO / STANDARD_SMALL / HIGHMEM_MEDIUM / HIGHMEM_XLARGESHARED_CORE_NANO
replicasnumber0
versionVALKEY_7_2 / VALKEY_8_0VALKEY_8_0
Ports
  • innetwork
  • inenv
Exports
hostporturlidentifier
Worth knowing
  • Needs the Network node's "Allow Memorystore for Valkey" switch, which creates the Private Service Connect policy a PSC endpoint requires. With it off, compile fails naming the switch rather than the apply failing twenty minutes in.
  • Beside Redis, never a replacement for it: the two are different resources on different networking. A graph that has Redis keeps Redis.
  • The engine version is pinned, like Redis's, so a provider default bump can never replace the instance under a graph that never asked for it.
  • The url export uses the redis:// scheme on purpose — Valkey speaks the same protocol, and every Redis client connects to it as-is.
MySQLgcp/cloud-sql-mysql@1
data

A managed MySQL instance on Cloud SQL, private IP only — the Postgres node with the engine swapped.

Creates

The Cloud SQL instance, a utf8mb4 database, a generated password on the write-only path, the master user with a host wildcard, and a Secret Manager secret holding that password. Backups and binary logging are explicitly on.

FieldTypeEditor default
namerequiredstringmaindb
versionMYSQL_8_4 / MYSQL_9_7MYSQL_8_4
tierstringdb-f1-micro
editionENTERPRISE / ENTERPRISE_PLUSENTERPRISE
Ports
  • innetwork
  • inenv
Exports
hostportdbNameusernamesecretIdsecretRefsecretVersionidentifier
Worth knowing
  • utf8mb4 rather than Cloud SQL's default utf8, which is MySQL's three-byte legacy charset and cannot store an emoji.
  • A MySQL user is a (name, host) pair; the master user's host is %, which on an instance with no public IP means any host inside the VPC.
  • The edition/tier pairing is enforced at compile time, same as Postgres: Enterprise Plus only runs on performance-optimized machines.
  • Only MySQL releases Oracle still supports will build: MYSQL_8_4 and MYSQL_9_7, both LTS. MySQL 8.0 reached end of life in April 2026, so it and its patch pins are refused at compile time rather than at apply, as is every 5.x and every superseded Innovation release.
  • Private IP only — needs a network edge to the same Network node as anything that connects to it.
Artifact Registrygcp/artifact-registry@1
storage

A private Docker repository in Artifact Registry — the push target for a build pipeline, and the registry your services pull from.

Creates

One Docker-format repository, with immutable tags when switched on.

FieldTypeEditor default
namerequiredstringimages
immutableTagsbooleanfalse
Ports
  • iniam
  • inenv
Exports
repositoryregistry
Worth knowing
  • A Cloud Run service pulling an image from a repository in its own project needs no edge at all — the Cloud Run service agent already reads every repository in the project.
  • So the iam edge means something stronger: this workload publishes images. It grants writer, which is what a build runner drawn as a Worker needs.
  • Immutable tags are off by default because a pipeline that re-tags latest on every build is the common case, and immutable tags refuse it.
Task queuegcp/tasks@1
data

A Cloud Tasks queue: a service enqueues, and Google delivers each task as an authenticated HTTP request to another Cloud Run service — with the rate limit and retries in the queue instead of the code.

Creates

Its own service account, and the queue with rate limits, bounded retries with backoff, and a queue-level HTTP target: every task is delivered to the connected service's host and this node's path, carrying an OIDC token minted as the queue's account. The traffic edge fills the host and audience and grants run.invoker; the iam edge grants cloudtasks.enqueuer on this one queue.

FieldTypeEditor default
namerequiredstringtasks
pathstring/tasks
rateLimitnumber100
concurrencynumber50
maxAttemptsnumber5
Ports
  • inenv
  • iniam
  • outtraffic
Exports
serviceAccountserviceAccountMemberqueuequeueNamerequestPath
Worth knowing
  • Two edges make it a queue: an iam edge from the enqueuing service (enqueue on this queue only), and a traffic edge to the handling service (host, audience, invoker). The env edge hands the enqueuer NAME_QUEUE, the full resource name a client library's CreateTask takes as its parent.
  • The handler can be private (public off): the queue calls it as its own account, which the traffic edge grants, and nothing else can. The audience is the service's bare URL, which is what Cloud Run validates a token against.
  • Enqueuing code needs no token and no serviceAccountUser: the OIDC identity is set at queue level, so CreateTask with a relative path is enough.
  • Retries are bounded (five attempts, 1s to 60s backoff) rather than Google's effectively unbounded default, so one bad handler cannot be hammered until someone notices.
  • Beside Queue (Pub/Sub) in the palette on purpose: both hold work between a producer and a consumer. Pub/Sub is pulled by a worker; Cloud Tasks pushes an HTTP request, which is why this node has a traffic port.
Static egress (NAT)gcp/nat@1
network

Cloud NAT with one reserved address, so every outbound request from the services behind it leaves from an IP a partner can allowlist.

Creates

A reserved external address, a Cloud Router on the VPC, and a Cloud NAT on that router using the address for this subnet only. The network edge from this node to a service fills its VPC interface as the Network node would and switches its egress to ALL_TRAFFIC, so traffic actually leaves through the NAT.

FieldTypeEditor default
namerequiredstringegress
loggingbooleanfalse
Ports
  • innetwork
Exports
ipnetworkIdsubnetId
Worth knowing
  • Draw it in the middle: Network → NAT (network), then NAT → service (network). The second edge is what routes the service through the NAT; a service connected to the Network node directly keeps Google-picked addresses.
  • Databases and caches stay on the Network node. They need the private services peering that node creates, and a network edge from the NAT to one is refused at compile with that advice.
  • Only this subnet is translated, never every subnet in the network, so nothing outside the graph rides the one static IP.
  • The address survives the NAT being rebuilt — it is reserved, not ephemeral — which is the point of having one someone else wrote down.
Functiongcp/function@1
compute

A Cloud Run function (2nd gen) from a zip in a bucket, built by Google and served by Cloud Run. Call it over HTTP, or draw a traffic edge from a Queue and it runs per message.

Creates

Its own service account, and the function: build config from the bucket object, a service config with memory, timeout, max instances and the account, and an allUsers invoker binding when public is on. A traffic edge from a Queue adds the Eventarc trigger and the two grants it needs.

FieldTypeEditor default
namerequiredstringhandler
sourceBucketrequiredstring
sourceObjectrequiredstring
runtimestringnodejs24
entryPointstringhandler
memorystring256Mi
timeoutnumber60
maxInstancesnumber3
publicbooleanfalse
envVarskey / value{}
Ports
  • outenv
  • outiam
  • intraffic
Exports
serviceAccountserviceAccountMemberurlfunctionName
Worth knowing
  • A traffic edge from a Queue is the event-driven shape: it puts an event trigger for the topic on the function (retrying failed deliveries) and grants the function's account eventarc.eventReceiver on the project and run.invoker on its own service. Miss either by hand and a function deploys and never fires — and the trigger takes up to two minutes to become active, which hides the miss for exactly long enough.
  • No network port. A 2nd-gen function attaches to a VPC only through a Serverless VPC Access connector, which the Network node deliberately stopped creating. A function reaches buckets, secrets, queues and public APIs; a private database is the Service node's job.
  • Secrets arrive as secret_environment_variables blocks resolved at start, keyed like the Service node's — NAME_PASSWORD — with the accessor grant on the function's account.
  • The env slot is a map (environment_variables) and env edges merge into it: a hand-typed variable with an injected name is overwritten.
  • Public means an allUsers invoker binding on the function's service — cloudfunctions2 has no invoker_iam_disabled switch — which a domain-restricted-sharing org policy refuses at apply.
  • The Queue's own pull subscription stays; Eventarc adds its own push subscription. A worker pulling and a function firing on the same topic each get every message.
Firewall (Cloud Armor)gcp/armor@1
security

Cloud Armor in front of the HTTPS load balancer: a per-IP rate limit with a ban, and Google's preconfigured SQL-injection and XSS rules, attached by a traffic edge from the load balancer.

Creates

One security policy: a rate-based ban rule (allow under the limit, 429 over it, then a ban for the configured minutes), two preconfigured WAF rules at sensitivity 1 denying with 403, and the required default rule, which allows. The traffic edge sets it as the security policy on the load balancer's backend service.

FieldTypeEditor default
namerequiredstringedge
rateLimitnumber600
banMinutesnumber10
wafbooleantrue
Ports
  • intraffic
Exports
policypolicyName
Worth knowing
  • Only meaningful with a traffic edge from an HTTPS load balancer (source: the load balancer; target: this node). The edge sets one attribute on the load balancer's backend service and touches nothing else.
  • Sensitivity 1 is the stable, low-false-positive tier of Google's preconfigured rules; higher levels trip on ordinary form posts. Turn the rules off if a legitimate client hits one.
  • The rate limit counts per client IP per minute; zero turns it off. A banned IP gets 429 for the ban duration, then starts again.
  • In the console, cv opens the policy by its name.
Static sitegcp/static-site@1
storage

A static site: a bucket served through the global HTTPS load balancer with Cloud CDN, at your domain. A Job or Service uploads it through an iam edge; a traffic edge to a Domain writes the DNS record and the certificate's hostname.

Creates

The bucket (uniform access, website settings, readable by allUsers), a reserved global address, a backend bucket with CDN on and a static-content cache policy, the URL map, a Google-managed certificate, the HTTPS proxy and forwarding rule, and the port-80 redirect chain — the load balancer node's chain with a backend bucket where the serverless NEG would be.

FieldTypeEditor default
namerequiredstringwww
indexDocumentstringindex.html
errorDocumentstring404.html
forceDestroybooleanfalse
Ports
  • iniam
  • inenv
  • outtraffic
Exports
bucketbucketNameregioniplbName
Worth knowing
  • The one public bucket in the catalogue, on purpose: a backend bucket serves objects with no credentials of its own, so allUsers gets objectViewer and public_access_prevention is left off. A domain-restricted-sharing org policy refuses the allUsers grant at apply.
  • Only meaningful with a traffic edge to a Domain — the certificate needs a hostname and DNS needs the address. Until DNS points at the address the certificate is a placeholder, which self-heals.
  • The bucket's own website endpoint is not used: it is HTTP only and cannot carry a certificate. The website settings still decide the index and not-found documents the backend bucket serves.
  • Cloud CDN caches static content for an hour by default and up to a day; invalidate through the load balancer after a deploy that must show immediately.
  • In the console, cv opens the load balancer's page, which is where traffic and the certificate's state are.
Uptime checkgcp/uptime@1
network

An uptime check with an email alert: Google probes a URL from several regions, and you get mail when it fails for five minutes.

Creates

An email notification channel, the uptime check (HTTPS on port 443, certificate validated, your path and interval), and an alert policy whose condition filters the check_passed metric on this check's id, counts the failures per region over twenty minutes, and fires when any region has seen one for five minutes.

FieldTypeEditor default
namerequiredstringwww
emailrequiredstring
pathstring/
period60 / 300 / 600 / 90060
timeoutnumber10
Ports
  • outtraffic
Exports
identifierrequestPath
Worth knowing
  • The traffic edge says what to watch: to a Domain, the public hostname — the address users see and the one whose DNS and certificate can fail — or to a Cloud Run service, its own URL. Without one, compile names the unfilled host.
  • The filter and aggregation are Google's own recipe; a hand-written policy that gets them wrong alerts never or always.
  • Intervals are the four Cloud Monitoring supports: 60, 300, 600 or 900 seconds. Anything else is refused at compile.
  • Filed under network beside Cron: it originates requests and holds no workload.
  • In the console, cv opens the check by its id.
Raw resourcegcp/raw@1
compute

Any resource the google provider supports, wired by hand. The bundled nodes are opinions — a database whose password never enters state, a network with its private-services peering already made. This is the other trade: exactly the one resource you name, none of those defaults, and the only way to reach the resource types no bundled node covers. Same shape as the AWS Raw resource, so learning one is learning both.

Creates

Exactly one resource, of the type you name, with the arguments you give it — nothing else, and no defaults added.

FieldTypeEditor default
resourcerequiredstring
attrsjson{}
blocksjson{}
exportskey / value{}
slotskey / value{}
iamjson{}
Ports
  • innetwork
  • inenv
  • iniam
  • intraffic
Exports
whatever you declare
Worth knowing
  • You declare the wiring. "Exports" maps a name other nodes can read to an attribute of this resource (dataset → dataset_id); "Slots" maps a name an edge may fill to one of its arguments. Until you declare them, this node connects to nothing.
  • A network edge fills the slot names it fills on bundled nodes: vpcNetwork (the network id), vpcSubnet (the subnet id), privateNetwork (the network, ordered after the private-services peering), pscNetwork (the network, ordered after the Private Service Connect policy). Declare the one your resource's argument means — a firewall's network is vpcNetwork.
  • Arguments and Nested blocks are separate fields because HCL treats them differently and the provider will not accept one for the other: a firewall's allow is a block, while its labels are an object-valued argument, and both look like a JSON object here. Blocks go in Nested blocks as [{ "type": "allow", "attrs": {…} }], repeated for repeated blocks, and nest via their own "blocks" key.
  • An env edge into a raw node injects every export you declared, under its own name — a dataset export on a node labelled analytics becomes ANALYTICS_DATASET. The label is the name argument, else the first *_id argument (dataset_id, repository_id, secret_id), else the resource type. Exports the closed vocabulary already knows (host, port, url, topic, project…) keep their usual suffix.
  • An iam edge needs the binding spelled out, because a Google grant is a resource of its own and no template exists for a type chosen at drawing time: the role, the google_…_iam_member resource that grants it, and which of its arguments names this resource (args, argument → attribute), plus any literal arguments it also needs (attrs, such as a location). Only additive _iam_member types are accepted, never an authoritative _iam_binding or _iam_policy.
  • No slot is ever required, because nothing here knows which of a given resource's arguments are mandatory — the provider's own validation is what catches a missing one, at plan time.
  • The permission preflight and the required-API check are keyed on resource type, so they cannot vouch for a raw resource or the grant an iam edge puts on it. If your credentials cannot create it, or its API is not enabled, the apply is where you find out.

Azure nodes

hashicorp/azurerm ~> 5.4

Twenty-eight nodes: twenty-three that create, four that look up what already exists, and Raw resource — any of the resource types the azurerm provider supports, wired by hand. Azure differs from both others in one structural way: every resource must live in a resource group, and the group is also the unit of deletion. The Network node creates it and hands it out through the network edge — which is why placement-dependent Azure nodes take one, even a storage account that has nothing networked about it — and it also owns the Container Apps environment every Container App, Worker and Job runs in. Every compute node runs as its own user-assigned identity, which is what iam edges grant to and what reads a generated database password out of Key Vault at start; the password itself never enters Terraform state. Network, Postgres, Storage and Key Vault each have an existing-resource sibling, and Import from Azure reads the subscription and places the references. In the console, cv opens any Azure node's resource in the portal — every node but Raw resource, whose type isn't known until you pick one.

Networkazure/vnet@1
network

The network — and the resource group everything else in the graph lives in, which is why every Azure node takes a network edge — plus the Container Apps environment every compute node runs in.

Creates

The resource group, the virtual network, the Container Apps infrastructure subnet and the Postgres delegated subnet, the private DNS zone and link that make a private database resolvable, a Log Analytics workspace for container logs, and one Container Apps environment on the infrastructure subnet. With mysqlAccess on, a MySQL delegated subnet and its own private zone too.

FieldTypeEditor default
workloadProfilesboolean
namerequiredstringmain
cidrstring10.0.0.0/16
mysqlAccessbooleanfalse
Ports
  • outnetwork
Exports
resourceGrouplocationvnetIdappSubnetIddbSubnetIddnsZoneIddnsLinkappEnvironmentIdlogWorkspaceIdmysqlSubnetIdmysqlDnsZoneIdresourceId
Worth knowing
  • The resource group is also Azure's unit of deletion — destroying this node's group is what tears the project down. OpenTofu state lives in a separate ackee-tfstate resource group that no graph creates or destroys.
  • One Container Apps environment per network, because an environment owns its infrastructure subnet outright and two cannot share one — every Container App, Worker and Job on this network runs inside it.
  • The delegations are mutually exclusive, so apps, Postgres and MySQL each need their own subnet; the node carves them up front. MySQL's is behind the mysqlAccess switch so a graph without MySQL does not pay for it.
Container Appazure/container-app@1
compute

A container on Azure Container Apps with ingress on its own HTTPS hostname, running as its own managed identity.

Creates

A user-assigned managed identity and the app itself in the network's Container Apps environment — ingress on the container port (external by default, internal with public off), min and max replicas, and the identity attached.

FieldTypeEditor default
namerequiredstringapi
imagerequiredstringmcr.microsoft.com/k8se/quickstart:latest
cpu0.25 / 0.5 / 1 / 20.5
memory0.5Gi / 1Gi / 2Gi / 4Gi1Gi
portnumber8080
minReplicasnumber1
replicasnumber2
publicbooleantrue
envVarskey / value{}
Ports
  • innetwork
  • intraffic
  • outenv
  • outiam
Exports
principalIdidentityIdappNameurlportresourceId
Worth knowing
  • The identity is user-assigned rather than system-assigned so it exists before the app does: a Key Vault reference from a database node is resolved at create time with it, and iam edges grant to it. The app is ordered after every grant its edges make.
  • An env edge from a database node lands the password as a Key Vault-backed Container Apps secret read by the identity; a Redis or Cosmos key lands as a secret value. Neither appears as a plain variable.
  • The exported url is the app's stable ingress hostname, scheme included — not the per-revision one, which changes on every deploy and breaks anything holding it.
Workerazure/worker@1
compute

A container app with no ingress: a process that pulls from a queue or watches a database, at a fixed instance count.

Creates

A user-assigned managed identity and a Container App in the network's environment with no ingress block, min and max replicas pinned to the instance count.

FieldTypeEditor default
namerequiredstringworker
imagerequiredstringmcr.microsoft.com/k8se/quickstart:latest
cpu0.25 / 0.5 / 1 / 20.5
memory0.5Gi / 1Gi / 2Gi / 4Gi1Gi
instancesnumber1
envVarskey / value{}
Ports
  • innetwork
  • outenv
  • outiam
Exports
principalIdidentityIdappNameresourceId
Worth knowing
  • No port, no hostname, no request-driven scaling. Instances are counted by hand; zero is a paused worker, not an error.
  • Env and iam edges work exactly as on a Container App — the same identity, the same secrets.
Jobazure/job@1
compute

A Container Apps job: a container that runs to completion, on a cron schedule or on demand.

Creates

A user-assigned managed identity and a Container App Job in the network's environment with a schedule trigger when a cron expression is set, a manual trigger otherwise; a timeout and a retry limit.

FieldTypeEditor default
namerequiredstringnightly
imagerequiredstringmcr.microsoft.com/k8se/quickstart:latest
cpu0.25 / 0.5 / 1 / 20.5
memory0.5Gi / 1Gi / 2Gi / 4Gi1Gi
timeoutnumber1800
retriesnumber1
schedulestring
envVarskey / value{}
Ports
  • innetwork
  • outenv
  • outiam
Exports
principalIdidentityIdjobNameresourceId
Worth knowing
  • On Azure the schedule is the job's own, so there is no separate scheduler node and no traffic edge — a job with a schedule is the scheduled job. Clear the schedule to run it on demand with az containerapp job start.
  • Timeout is per execution; retries re-run a failed execution up to the limit.
Postgresazure/postgres@1
data

A managed Postgres Flexible Server on the network's delegated database subnet, private by construction, with a generated password that never enters state.

Creates

The server — public access off, 7-day backup retention — and a database with UTF8 encoding; an ephemeral random password written through write-only arguments to the server and to a secret in a small Key Vault of the node's own, on access policies, with a policy for the deploying identity.

FieldTypeEditor default
namerequiredstringmaindb
version15 / 16 / 1716
skuNameB_Standard_B1ms / B_Standard_B2s / GP_Standard_D2s_v3B_Standard_B1ms
storageMbnumber32768
Ports
  • innetwork
  • inenv
Exports
hostportdbNameusernamesecretNamesecretIdsecretRefvaultIdresourceIdidentifier
Worth knowing
  • The password is generated by an ephemeral random_password and reaches the server and the vault through write-only arguments, so it is never in Terraform state or plan files. Rotate secrets on the project and the next deploy rewrites both in one apply.
  • An env edge to a Container App, Worker or Job adds an access policy letting the workload's identity read the vault, a Key Vault-backed secret on the workload, and MAINDB_PASSWORD reading it. Only those three node types can take the edge.
  • The node's vault uses access policies rather than RBAC because a policy is a control-plane write Contributor can make; an RBAC data role is not.
  • Waits for the network's private DNS link before it is created — azurerm documents the dependency, and the network edge adds it.
MySQLazure/mysql@1
data

A managed MySQL Flexible Server on the network's MySQL delegated subnet — the Postgres node with the engine swapped.

Creates

The server with a storage block, and a utf8mb4 database; the same ephemeral password, write-only arguments and node-owned Key Vault as Postgres.

FieldTypeEditor default
namerequiredstringmaindb
version5.7 / 8.0.21 / 8.48.0.21
skuNameB_Standard_B1ms / B_Standard_B2s / GP_Standard_D2ds_v4B_Standard_B1ms
storageGbnumber20
Ports
  • innetwork
  • inenv
Exports
hostportdbNameusernamesecretNamesecretIdsecretRefvaultIdresourceIdidentifier
Worth knowing
  • Needs the Network node's mysqlAccess switch on: MySQL cannot share Postgres's delegated subnet or private zone, and the compile error names the switch.
  • Everything the Postgres node says about the password and the env edge applies here unchanged.
Redisazure/redis@1
data

Azure Cache for Redis on the Basic or Standard tier, reached over TLS with an access key.

Creates

The cache — TLS 1.2 minimum, plaintext port off, size C0–C6.

FieldTypeEditor default
namerequiredstringcache
skuBasic / StandardBasic
capacity0 / 1 / 2 / 3 / 4 / 5 / 60
Ports
  • innetwork
  • inenv
Exports
hostportpasswordcacheUrlidentifierresourceId
Worth knowing
  • An env edge injects CACHE_HOST and CACHE_PORT as values and CACHE_PASSWORD and CACHE_URL (the rediss:// form with the key) as Container Apps secrets.
  • Public endpoint gated by the key. Private connectivity on these tiers is a private endpoint plus its own DNS zone, a product decision rather than a default; Premium with VNet injection is a Raw resource use case.
  • The access key is a computed attribute the provider reads, so it does sit in state, as on every cloud's cache node.
Queueazure/queue@1
data

A Service Bus queue in its own Basic-tier namespace: peek-lock, ordered delivery, dead-lettering, a delivery count.

Creates

The namespace (TLS 1.2 minimum) and the queue with its lock duration, message TTL, max delivery count and dead-lettering on expiry.

FieldTypeEditor default
namerequiredstringjobs
maxDeliveryCountnumber10
lockDurationstringPT1M
messageTtlstringP14D
Ports
  • innetwork
  • inenv
  • iniam
Exports
hostqueueNamearnidentifierresourceId
Worth knowing
  • An env edge injects JOBS_HOST — the fully qualified namespace every SDK takes beside a credential — and JOBS_QUEUE. No connection string: the identity is the credential.
  • An iam edge grants both Azure Service Bus Data Sender and Data Receiver on the queue; a queue node cannot tell an API from a worker, and narrowing is a Raw resource job.
  • Basic tier has no topics or sessions. Standard is a Raw resource change on the namespace.
Registryazure/registry@1
storage

An Azure Container Registry the graph's own workloads pull from with their identity.

Creates

The registry, admin user off.

FieldTypeEditor default
namerequiredstringimages
skuBasic / Standard / PremiumBasic
Ports
  • innetwork
  • inenv
  • iniam
Exports
registryarnresourceId
Worth knowing
  • An iam edge from a Container App, Worker or Job grants AcrPull and attaches the registry to the workload, so a private image pulls with the managed identity and no stored password.
  • An env edge injects IMAGES_SERVER, the login server, for a builder that pushes.
  • Registry names are global and alphanumeric; the hash suffix accounts for it.
Cosmos DBazure/cosmos@1
data

A serverless Cosmos DB account with one NoSQL API database — the document store beside Firestore and DynamoDB.

Creates

The account — serverless, Session consistency, TLS 1.2, one region — and the SQL database.

FieldTypeEditor default
namerequiredstringdocs
freeTierbooleanfalse
Ports
  • innetwork
  • inenv
  • iniam
Exports
endpointdbNamepasswordarnaccountNameidentifierresourceId
Worth knowing
  • An env edge injects DOCS_ENDPOINT and DOCS_NAME as values and DOCS_PASSWORD (the primary key) as a secret. An iam edge grants the built-in Data Contributor role on the account to the workload's identity — the keyless path a production app should use.
  • Serverless has no throughput to size and no free-tier discount; the free tier is a per-subscription switch on the account.
  • Account creation takes several minutes; a first apply is slower than the other data nodes.
Storageazure/storage@1
storage

A private blob container inside a locked-down storage account.

Creates

The storage account — TLS 1.2 minimum, HTTPS only, nested public access off, versioning when on — and one private blob container.

FieldTypeEditor default
namerequiredstringassets
versioningbooleantrue
Ports
  • innetwork
  • iniam
  • inenv
Exports
arnaccountNamebucketurlresourceId
Worth knowing
  • Storage account names are globally unique, lowercase alphanumeric, and capped at 24 characters — the tightest naming rule in any of the three clouds; the deterministic hash suffix accounts for it. Container names are sanitised to the Blob service's 3–63 lowercase rule.
  • Unlike S3 and GCS this node takes a network edge — not for networking, but because an Azure storage account must live in a resource group and location.
  • An iam edge grants Storage Blob Data Contributor on the account to the workload's identity.
Static siteazure/static-site@1
storage

An Azure Static Web App: the built site served from Azure's edge with HTTPS on its own hostname.

Creates

The Static Web App on the Free or Standard tier.

FieldTypeEditor default
namerequiredstringwww
tierFree / StandardFree
location / westus2 / centralus / eastus2 / westeurope / eastasia
Ports
  • innetwork
  • intraffic
Exports
urlidentifierresourceId
Worth knowing
  • Content is pushed with the SWA CLI or a GitHub Actions workflow using the site's deployment token; Ackee creates the site and nothing more.
  • Static Web Apps exist in a handful of regions. Set the node's region when the network's is not one of them, or the plan fails on location.
Key Vaultazure/key-vault@1
security

A Key Vault for secret material. You set values out of band; apps read them with their managed identity.

Creates

The vault only, with RBAC authorization on — never a secret value.

FieldTypeEditor default
namerequiredstringapi-key
descriptionstring
Ports
  • innetwork
  • inenv
  • iniam
Exports
arnvaultIdvaultNamevaultUrisecretRefresourceId
Worth knowing
  • Values are never set through Ackee or Terraform — that would put them in state in plaintext.
  • An env edge injects the vault's name and URI; an iam edge grants Key Vault Secrets User to the workload's identity. Draw both: on Azure the env edge alone grants nothing.
  • Role assignments need Role Based Access Control Administrator beside Contributor, which the Requirements tab says when the graph has one.
Functionazure/function@1
compute

Runs a container containing Azure Functions code on an Elastic Premium plan. Connect a Network for placement and draw env/iam connections to the services the function uses.

Creates

Linux Function App, Linux Premium service plan, storage account and user-assigned identity.

FieldTypeEditor default
namerequiredstringfunction
imagerequiredstringmcr.microsoft.com/azure-functions/node:4-node22
skuEP1 / EP2 / EP3EP1
envVarskey / value{}
Ports
  • innetwork
  • intraffic
  • outenv
  • outiam
Exports
urlhostfunctionNameprincipalIdresourceId
Worth knowing
  • EP1 has a fixed running cost even when idle. This is a Function runtime container, not an arbitrary web-app image.
  • Images must use a tag. Private ACR images need an iam edge to their Registry. The network edge supplies placement, not VNet integration for Functions.
  • Code must configure its own HTTP authorization and Service Bus bindings. An env edge supplies connection settings; it does not generate function code.
HTTP scheduleazure/cron@1
network

Calls an HTTP endpoint at a recurring interval using a Logic App. Connect its traffic edge to a public Service, Function, Front Door, Domain or Static Site.

Creates

Logic App workflow, recurrence trigger and HTTP action.

FieldTypeEditor default
namerequiredstringschedule
frequencyMinute / Hour / Day / WeekHour
intervalnumber1
pathstring/
methodGET / POST / PUT / PATCH / DELETEPOST
bodystring
Ports
  • innetwork
  • outtraffic
Exports
scheduleNameresourceId
Worth knowing
  • This schedules HTTP requests; use Job for a scheduled container command.
  • The target must accept anonymous requests. For an authenticated endpoint, use a raw Logic App action with authentication configured.
  • Request bodies are configuration, not a secret store.
Load Balancerazure/https-lb@1
network

Routes public HTTPS traffic through Azure Front Door with health probes and HTTP-to-HTTPS redirects. Draw a traffic edge from it to a Service, Function or Static Site.

Creates

Front Door profile, endpoint, origin group, origin and route.

FieldTypeEditor default
namerequiredstringfrontdoor
skuStandard_AzureFrontDoor / Premium_AzureFrontDoorStandard_AzureFrontDoor
healthPathstring/
Ports
  • innetwork
  • intraffic
Exports
urlhostresourceId
Worth knowing
  • The backend must expose public HTTPS ingress. This node does not create private-link origins or an internal load balancer.
  • Managed WAF rules require the Premium tier. Front Door has a fixed base cost plus usage charges.
Domain + TLSazure/domain@1
network

Adds a custom hostname and Azure-managed TLS to Front Door. Connect Domain → Load Balancer using a traffic edge.

Creates

Front Door custom domain and route association; optional Azure DNS TXT and CNAME records.

FieldTypeEditor default
hostnamerequiredstringapp.example.com
dnsZoneNamestring
dnsResourceGroupstring
Ports
  • intraffic
Exports
urlhostdnsValidationTokendnsTxtNameresourceId
Worth knowing
  • For automatic DNS, supply an existing Azure DNS zone and its resource group. The hostname must be a subdomain of that zone.
  • Otherwise, add the exported TXT validation token and a CNAME to the Front Door hostname in your DNS provider. TLS becomes ready after DNS validation and propagation.
  • Automatic apex-domain records are not supported.
Web Firewallazure/waf@1
security

Protects a Premium Front Door and every connected custom domain with managed web-attack and bot rules. Connect WAF → Load Balancer with a traffic edge.

Creates

Front Door WAF policy and its security-policy association.

FieldTypeEditor default
namerequiredstringfirewall
modePrevention / DetectionPrevention
Ports
  • innetwork
  • outtraffic
Exports
policyNameresourceId
Worth knowing
  • Requires Premium_AzureFrontDoor. Detection logs matches; Prevention blocks them.
  • One WAF node attaches to one Front Door. A WAF node without a traffic connection protects nothing.
Static egressazure/nat@1
network

Provides one static public outbound IP for the Network node’s app subnet. Connect Network → Static egress.

Creates

Standard NAT gateway, Standard static public IP and gateway/IP/subnet associations.

FieldTypeEditor default
namerequiredstringegress
idleTimeoutnumber4
Ports
  • innetwork
Exports
hostresourceId
Worth knowing
  • Enable workloadProfiles on a managed Network. Legacy consumption-only environments do not support NAT. Only outbound traffic through the app subnet is covered. Functions placed in the resource group are not automatically VNet-integrated.
  • Only one NAT gateway can own a subnet. Gateways and processed data incur charges.
Uptime checkazure/uptime@1
network

Checks an HTTPS endpoint from three regions and emails an alert when two locations fail. Connect Network → Uptime and Uptime → the endpoint.

Creates

Workspace-based Application Insights, standard web test, email action group and availability metric alert.

FieldTypeEditor default
namerequiredstringuptime
emailrequiredstring
pathstring/
period300 / 600 / 900300
Ports
  • innetwork
  • outtraffic
Exports
resourceId
Worth knowing
  • Expects HTTP 200 and a valid TLS certificate. The endpoint must be public.
  • Set a real alert email. Checks run every 5, 10 or 15 minutes and incur monitoring charges.
Publish / Subscribeazure/pubsub@1
data

Provides a Service Bus topic and a durable subscription for fan-out messaging. Env edges inject the namespace host, topic and subscription; iam edges grant send and receive access.

Creates

Standard Service Bus namespace, topic and subscription with dead-lettering.

FieldTypeEditor default
namerequiredstringevents
subscriptionstringworker
maxDeliveryCountnumber10
Ports
  • innetwork
  • inenv
  • iniam
Exports
hosttopicsubscriptionNameresourceId
Worth knowing
  • Uses managed-identity authentication; namespace local key authentication is disabled.
  • Consumer code must use the Service Bus SDK or a Functions Service Bus binding. This node does not configure an HTTP push subscription.
Emailazure/email@1
network

Transactional email through Azure Communication Services with an Azure-managed sending domain.

Creates

Communication Service, Email Communication Service, an Azure-managed domain and its association.

FieldTypeEditor default
namerequiredstringmail
dataLocationUnited States / Europe / UK / AustraliaUnited States
Ports
  • innetwork
  • inenv
Exports
endpointsenderDomainresourceId
Worth knowing
  • Connect Network for the resource group; dataLocation separately controls communication data residency.
  • An env edge injects MAIL_ENDPOINT, MAIL_SENDER_DOMAIN and MAIL_CONNECTION_STRING. Container Apps store the connection string as a secret; Function app settings and Terraform state contain it. The connection string is never a deployment output.
  • Your code sends email using the Azure Communication Email SDK. Use the allowed sender address shown by Azure for the managed domain. This node does not run a mail server or provision inboxes.
  • Azure sending quotas apply. Custom branded domains and DNS verification are not yet built into this node. No iam edge is required because the SDK uses the injected connection string.
Event Streamazure/eventhub@1
data

A partitioned event stream for telemetry, analytics and replayable application events.

Creates

Standard Event Hubs namespace, event hub and a named consumer group.

FieldTypeEditor default
namerequiredstringevents
partitionsnumber2
retentionDaysnumber1
capacitynumber1
consumerGroupstringworker
Ports
  • innetwork
  • inenv
  • iniam
Exports
hosteventHubNameconsumerGrouparnresourceId
Worth knowing
  • An env edge injects EVENTS_HOST, EVENTS_EVENT_HUB and EVENTS_CONSUMER_GROUP. An iam edge grants the workload identity Azure Event Hubs Data Sender and Data Receiver on the hub. Draw both.
  • SAS authentication is disabled. Use an Azure Identity credential with the Event Hubs SDK.
  • The namespace has a public TLS endpoint; a Network edge supplies placement, not private networking.
  • Configure 1–32 partitions, 1–7 days of retention and 1–20 throughput units. Standard has a provisioned capacity charge.
  • Consumers must implement processing and checkpointing. Attach Storage separately for a blob checkpoint store; this node does not run consumers or enable Capture.
Network (existing)azure/vnet-ref@1
network

An existing resource group, network and Container Apps environment, looked up rather than created.

Creates

Nothing. Data sources for the resource group, the virtual network, the Container Apps environment and, when named, the Postgres delegated subnet and private DNS zone.

FieldTypeEditor default
resourceGrouprequiredstring
vnetrequiredstring
appEnvironmentrequiredstring
dbSubnetstring
dnsZonestring
Ports
  • outnetwork
Exports
resourceGrouplocationvnetIdappEnvironmentIddbSubnetIddnsZoneIdresourceId
Worth knowing
  • The Container Apps environment is required: every compute node runs inside one, and one cannot be created into a network somebody else owns. Import from Azure fills it in when it can find one on the network.
  • Without dbSubnet and dnsZone a managed Postgres cannot attach, and the compile error says which fields to set.
  • MySQL cannot attach to a reference network yet — it needs a Network node with mysqlAccess on.
Postgres (existing)azure/postgres-ref@1
data

An existing Postgres Flexible Server, looked up by name.

Creates

Nothing. A data source for the server.

FieldTypeEditor default
resourceGrouprequiredstring
serverrequiredstring
dbNamestringpostgres
usernamestring
passwordSecretIdstring
Ports
  • inenv
Exports
hostportdbNameusernamesecretNamesecretIdsecretRefidentifierresourceId
Worth knowing
  • The database defaults to postgres and the user to the server's admin login; set both to what the app uses.
  • Naming a Key Vault secret id turns the env edge's password injection back on, as a Key Vault-backed secret the workload's identity reads. The reference cannot grant on a vault it does not own — draw an iam edge to a Key Vault (existing) node for an RBAC vault, or set an access policy by hand.
Storage (existing)azure/storage-ref@1
storage

An existing storage account and one of its blob containers, looked up by name.

Creates

Nothing. Data sources for the account and the container.

FieldTypeEditor default
resourceGrouprequiredstring
accountrequiredstring
containerrequiredstring
Ports
  • iniam
  • inenv
Exports
arnaccountNamebucketurlresourceId
Worth knowing
  • Import from Azure lists one row per container, so an account with several is several choices.
  • Ackee's own ackee-tfstate state accounts are never offered.
Key Vault (existing)azure/key-vault-ref@1
security

An existing Key Vault, looked up by name.

Creates

Nothing. A data source for the vault.

FieldTypeEditor default
resourceGrouprequiredstring
vaultrequiredstring
Ports
  • inenv
  • iniam
Exports
arnvaultIdvaultNamevaultUrisecretRefresourceId
Worth knowing
  • The iam edge's role assignment assumes the vault uses RBAC authorization. A vault on access policies needs its policy set by hand, and Import from Azure says which kind each vault is.
Raw resourceazure/raw@1
compute

Any single resource the pinned azurerm provider supports, wired by hand — the second tier of the catalogue.

Creates

Exactly one resource, of the type named, with the arguments and nested blocks given. Nothing else, and nothing added.

FieldTypeEditor default
resourcerequiredstring
attrsjson{}
blocksjson{}
exportskey / value{}
slotskey / value{}
iamRolestring
Ports
  • innetwork
  • inenv
  • iniam
Exports
whatever exports declaresiamRole
Worth knowing
  • It connects to nothing until you declare something: exports for env edges, slots for the network edge. Declare resourceGroup → resource_group_name and location → location and the Network node places it; the other slot names the edge knows are subnet, dbSubnet, dnsZone, appEnvironment, mysqlSubnet and mysqlDnsZone.
  • An iam edge needs iamRole — a built-in role name — and an arn export to scope it to; it becomes one role assignment, as on a bundled node.
  • Blocks are declared separately from arguments, and the difference matters: an argument written where a block belongs parses and is then rejected at plan time.
  • The permission preflight and the resource-provider check are keyed on resource type, so they cannot vouch for a raw resource. If your credentials cannot create it, or its provider is not registered, the apply is where you find out.