Skip to main content
The recommended AWS path is Helm on Amazon EKS with Amazon RDS for MySQL. For the smallest first deployment, use EKS Auto Mode and Kubernetes LoadBalancer Services so AWS provisions Network Load Balancers for Den web and Den API. Move to an ALB or an existing ingress platform when you need shared layer-7 routing, WAF policy, or more advanced certificate handling.

What AWS manages

  • EKS control plane, compute, pod networking, and load-balancer integration
  • VPCs, subnets, routing, security groups, and IAM
  • RDS for MySQL, encryption, backups, and failover
  • Route 53 DNS and ACM certificates when you use those services
The OpenWork chart manages Deployments, Services, ConfigMaps, Secret references, probes, and the database migration Job.

AWS checklist

  1. Create an EKS Auto Mode cluster with eksctl 0.195.0 or newer.
  2. Create an RDS MySQL 8-compatible database in private subnets.
  3. Allow database port 3306 only from the EKS workload or node security boundary.
  4. Store DATABASE_URL, BETTER_AUTH_SECRET, and DEN_DB_ENCRYPTION_KEY in a Kubernetes Secret.
  5. Start from the chart’s values.aws-load-balancer.yaml example.
  6. Install the chart, verify migrations and readiness, then point DNS at the generated load balancers.
  7. Enable trusted HTTPS before creating the first administrator.
For exact CLI commands, values, DNS/TLS choices, migration troubleshooting, verification, and cleanup, follow the AWS EKS operator runbook.

ECS Fargate with Terraform

These instructions and historical contract-v1 live results apply to direct Terraform users. The managed AWS installer now consumes the same module with explicit legacy state-backed secrets, a maintenance-first runner fence and separate customer IAM re-approval. It does not silently convert existing keys to write-only mode. Its real version-increasing Update/fresh-install/destroy proof is a separate gate, not certified by the direct-user results below. See the managed installer upgrade notes.

What it deploys

If you run containers on ECS Fargate rather than Kubernetes, the Terraform module creates an ECS cluster (or uses yours), Den API and Den web as Fargate services behind an HTTPS Application Load Balancer, private RDS for MySQL, optional ElastiCache for Redis, and AWS Secrets Manager references. One standalone migration task must finish with exit 0 before API or web rollout. Application replicas do not run migrations. A small native Lambda invocation starts the task and observes completion; no local-exec provisioner is required. API defaults to 1 vCPU / 2 GiB and web to 0.5 vCPU / 1 GiB. Managed RDS deletion protection defaults on. The 0.18.57/0.18.58 API/web defaults are digest-pinned; other versions and enabled optional services require digest-pinned image overrides. Database TLS verifies the certificate chain using the public regional RDS CA bundle, loaded before Node starts. The application’s current MySQL parser does not enable mysql2’s separate hostname identity check; full hostname verification remains an application follow-up, not a claim of this module.

Tested scenarios

Support level

OpenWork’s Terraform modules are beta. If you hit an issue:
  • Customers with a support agreement: contact your OpenWork team through your usual support channel.
  • Everyone else: open a GitHub issue.
  • Security issues: report them privately (SECURITY.md).
The table records the tested core only, not production load, backup restoration, legacy handover, or every optional component. Complete your review before pinning a source commit. The module and its deployment-contract improvements are available on dev in beta; the managed installer has separate approval and live-verification gates. See the shared module contract for baseline checks and known limits.

Upgrade notes

Database sizing default: db.t4g.micro → db.t4g.small (2 GiB memory). Existing direct Terraform users who omitted database.instance_class will see an in-place instance-class change in their next plan. Apply schedules it for the next RDS maintenance window by default (apply_immediately = false); the class change restarts the database and interrupts connections when performed. With apply_immediately = true, it restarts during apply instead. Review pending modifications and arrange maintenance before applying. AWS documents modification timing here. Estimated Single-AZ MySQL instance cost in us-east-1 rises from **11.68to11.68 to 23.36/month (+11.68)∗∗at730hours(11.68)** at 730 hours (0.016 → $0.032/hour, public list prices checked 2026-10-19). This excludes storage, CPU credits, backups, networking, taxes and discounts; see RDS pricing. To keep the old size, add this to the module call before upgrading, merging it into your existing database object without dropping other settings:
Micro retains its low-memory risk. API/web defaults and managed installer size pins are unchanged. See the Small / trial table for the cross-cloud baseline; it is not a user-capacity guarantee. The first upgrade from a per-task migration module requires maintenance: freeze independent deployers and retries, scale the old API to zero, disable replacement/autoscaling and manual legacy launches, wait for all old tasks and DB sessions/in-flight DDL to terminate, and fence legacy DDL credentials where feasible. Only then apply the new module with the target replica count. Reducing to one task alone is not an exclusion barrier. Do not restore a legacy migration-capable task definition during rollback. This orders deployments; it does not add the application-level migration lock, dirty marker or schema compatibility protocol. Existing inputs remain, including the deprecated nested database.deletion_protection alias. Floating image tags are now rejected: pin the current digests before upgrading. Use one state/release coordinator per physical database. Migration failure or an unknown outcome blocks rollout and requires inspection, not automatic SQL retry. See the module upgrade notes for the full fence and secret-conversion procedure.

State guidance

Use Terraform or OpenTofu >= 1.11, < 2.0 with encrypted, access-controlled remote state. An S3 root backend should set encrypt = true and use_lockfile = true; do not create a new DynamoDB locking table. The reusable module configures neither a provider nor a backend and ships no cross-registry lock file. Generate and retain the root lock with your chosen tool. This direct-user module uses the live-tested AWS ~> 6.69 provider line and random ~> 3.9; the previous cross-major compatibility exception is no longer needed. The managed AWS root now locks AWS 6.69.1 and random 3.9.1 and explicitly retains legacy state-backed secrets/current strict SecretVersion. Its updated bootstrap grants only deployment-scoped migration Lambda, role and log permissions and requires customer re-approval plus a maintenance window. The runner drains old tasks before migration and verifies completed strict deployments; retained legacy revisions must never be started manually. Synthetic state tests do not replace a real provider-upgrade proof or managed-deployments owner review. For new installs, use write_only_secrets = true with persistent core values supplied through the ephemeral secret_values_wo input: RDS and Secrets Manager write-only fields keep those values out of new state snapshots. Write-only mode is the new default. Existing installations must explicitly set write_only_secrets = false before upgrading to retain their current keys/state, or securely preserve and supply the exact existing keys and DB password before converting. Missing ephemeral values fail validation before changes, rather than silently regenerating auth/encryption keys. Never regenerate an encryption key during an infrastructure upgrade. Optional integration secrets retain their state-backed compatibility path; restrict state history accordingly. Secret settings belong in extra_secrets, not plain extra_environment.

Cost notes

Costs depend on region, replica count and egress: Fargate API/web, RDS, ALB, optional NAT/Redis, image pulls for the standalone migrator, and brief Lambda duration. A two-API-task lab with one NAT costs more than the one-task defaults. Stop disposable stacks promptly and disable deletion protection explicitly only for those tests. You provide a VPC, two hostnames (the web host and api. plus the web host), and an ACM certificate or a Route 53 zone the module can use to create one. HTTPS is required: Den API does not start with a plain-HTTP public URL. Email is optional for setup and sign-in, but member invites and password reset need SMTP or Resend. The module can also run OpenWork Web, so members chat in the browser with each workspace in a Daytona sandbox: set openwork_web_enabled = true and provide a Daytona API key and sandbox snapshot. It can also run the headless runner, so the Slack assistant, headless Automations and Workbot (a chat app at chat. plus your web host) work on your deployment with your own model provider: set headless_runner, workbot and slack_assistant_enabled. See the module README for inputs, a complete example, and cost notes. Next: Create the first administrator.