DEV Community

Otto
Otto

Posted on

Terraform for Indie Hackers in 2026: 5 Production Templates You Can Deploy in 10 Minutes

You built the product. Now it needs to run somewhere reliable.

Manually clicking through AWS consoles works for demos. It breaks at 3 AM. It fails when you need a staging environment. Infrastructure as Code (IaC) with Terraform fixes all of this.

Here are 5 production-ready Terraform templates for indie hackers and solo devs.


Why Terraform Over Pulumi/CDK/SST?

  • HCL is readable — even non-devs can review your infra
  • 15,000+ free modules in the registry
  • Providers for everything — AWS, GCP, Cloudflare, Vercel, GitHub
  • 100% free — OpenTofu is the open-source fork with zero lock-in
  • Massive community — every error is already solved on Stack Overflow

The 4 Commands You'll Use 95% of the Time

terraform init    # Download providers
terraform plan    # Preview changes (ALWAYS run first)
terraform apply   # Create/update resources
terraform destroy # Remove everything
Enter fullscreen mode Exit fullscreen mode

Template 1: AWS Minimal SaaS Stack (~$15-30/month)

Single server + PostgreSQL. Handles up to ~1K concurrent users.

terraform {
  required_providers {
    aws = { source = "hashicorp/aws", version = "~> 5.0" }
  }
}

provider "aws" { region = var.aws_region }

variable "aws_region"  { default = "eu-west-3" }
variable "app_name"    { type = string }
variable "db_password" { sensitive = true; type = string }

resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  tags = { Name = "${var.app_name}-vpc" }
}

resource "aws_instance" "app" {
  ami           = "ami-0c02fb55956c7d316"
  instance_type = "t3.micro"
  subnet_id     = aws_subnet.public_a.id
  vpc_security_group_ids = [aws_security_group.web.id]

  user_data = <<-EOF
    #!/bin/bash
    yum update -y && yum install -y docker
    systemctl start docker && systemctl enable docker
  EOF

  tags = { Name = "${var.app_name}-server" }
}

resource "aws_eip" "app" {
  instance = aws_instance.app.id
  domain   = "vpc"
}

resource "aws_db_instance" "main" {
  engine                  = "postgres"
  engine_version          = "16.1"
  instance_class          = "db.t3.micro"
  identifier              = "${var.app_name}-db"
  username                = "dbadmin"
  password                = var.db_password
  allocated_storage       = 20
  storage_encrypted       = true
  deletion_protection     = true
  backup_retention_period = 7
  skip_final_snapshot     = false
}

output "server_ip"   { value = aws_eip.app.public_ip }
output "db_endpoint" { value = aws_db_instance.main.endpoint }
Enter fullscreen mode Exit fullscreen mode

Deploy in ~8 minutes:

terraform init
terraform plan -var="app_name=my-saas" -var="db_password=your-secret"
terraform apply
Enter fullscreen mode Exit fullscreen mode

Template 2: GCP Cloud Run (Serverless — ~$0-5/month)

Zero server management. Perfect for early-stage MVPs.

terraform {
  required_providers {
    google = { source = "hashicorp/google", version = "~> 5.0" }
  }
}

provider "google" {
  project = var.project_id
  region  = "europe-west1"
}

resource "google_project_service" "run" {
  service = "run.googleapis.com"
}

resource "google_cloud_run_service" "app" {
  name     = var.app_name
  location = "europe-west1"

  template {
    spec {
      containers {
        image = var.docker_image
        resources { limits = { cpu = "1000m", memory = "512Mi" } }
      }
    }
    metadata {
      annotations = {
        "autoscaling.knative.dev/maxScale" = "10"
        "autoscaling.knative.dev/minScale" = "0"
      }
    }
  }

  traffic {
    percent         = 100
    latest_revision = true
  }
}

# Public access
resource "google_cloud_run_service_iam_member" "public" {
  service  = google_cloud_run_service.app.name
  location = "europe-west1"
  role     = "roles/run.invoker"
  member   = "allUsers"
}

output "url" { value = google_cloud_run_service.app.status[0].url }
Enter fullscreen mode Exit fullscreen mode

Template 3: Cloudflare + Vercel ($0/month)

terraform {
  required_providers {
    cloudflare = { source = "cloudflare/cloudflare", version = "~> 4.0" }
    vercel     = { source = "vercel/vercel",          version = "~> 1.0" }
  }
}

provider "cloudflare" { api_token = var.cloudflare_api_token }
provider "vercel"     { api_token = var.vercel_api_token }

resource "vercel_project" "app" {
  name      = var.domain_name
  framework = "nextjs"
  git_repository = { type = "github", repo = var.github_repo }
}

resource "cloudflare_record" "root" {
  zone_id = var.cloudflare_zone_id
  name    = "@"
  type    = "CNAME"
  value   = "${vercel_project.app.name}.vercel.app"
  proxied = true
}

resource "cloudflare_page_rule" "https" {
  zone_id  = var.cloudflare_zone_id
  target   = "http://${var.domain_name}/*"
  priority = 1
  actions  { always_use_https = true }
}
Enter fullscreen mode Exit fullscreen mode

Template 4: Multi-Environment (Staging + Production)

terraform/
├── modules/app/         # Shared module
└── environments/
    ├── staging/main.tf  # t3.micro, 1 instance
    └── production/main.tf  # t3.small, 2+ instances
Enter fullscreen mode Exit fullscreen mode
# environments/staging/main.tf
module "app" {
  source        = "../../modules/app"
  environment   = "staging"
  instance_type = "t3.micro"
  min_capacity  = 1
  max_capacity  = 2
  domain_name   = "staging.myapp.com"
}

# environments/production/main.tf
module "app" {
  source        = "../../modules/app"
  environment   = "production"
  instance_type = "t3.small"
  min_capacity  = 2
  max_capacity  = 10
  domain_name   = "myapp.com"
}
Enter fullscreen mode Exit fullscreen mode

Separate state per environment:

cd environments/staging && terraform apply    # test here first
cd environments/production && terraform apply  # then promote
Enter fullscreen mode Exit fullscreen mode

Template 5: ECS Fargate Auto-Scaling (~$40-80/month)

Production-grade containers without managing Kubernetes.

resource "aws_ecs_cluster" "main" {
  name = "${var.app_name}-cluster"
  setting { name = "containerInsights"; value = "enabled" }
}

resource "aws_ecs_task_definition" "app" {
  family                   = var.app_name
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = 256
  memory                   = 512

  container_definitions = jsonencode([{
    name  = var.app_name
    image = "${aws_ecr_repository.app.repository_url}:latest"
    portMappings = [{ containerPort = 3000 }]
    logConfiguration = {
      logDriver = "awslogs"
      options = {
        "awslogs-group"         = "/ecs/${var.app_name}"
        "awslogs-region"        = var.aws_region
        "awslogs-stream-prefix" = "ecs"
      }
    }
  }])
}

# Auto-scale at 75% CPU
resource "aws_appautoscaling_policy" "cpu" {
  name               = "${var.app_name}-cpu-scaling"
  policy_type        = "TargetTrackingScaling"
  resource_id        = aws_appautoscaling_target.ecs.resource_id
  scalable_dimension = aws_appautoscaling_target.ecs.scalable_dimension
  service_namespace  = aws_appautoscaling_target.ecs.service_namespace

  target_tracking_scaling_policy_configuration {
    target_value = 75.0
    predefined_metric_specification {
      predefined_metric_type = "ECSServiceAverageCPUUtilization"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

5 Non-Negotiable Rules

1. Never commit secrets

# .gitignore
*.tfvars
*.tfstate
.terraform/
Enter fullscreen mode Exit fullscreen mode

2. Always plan before apply

terraform plan -out=tfplan && terraform apply tfplan
Enter fullscreen mode Exit fullscreen mode

3. Enable deletion protection

resource "aws_db_instance" "main" {
  deletion_protection = true
  lifecycle { prevent_destroy = true }
}
Enter fullscreen mode Exit fullscreen mode

4. Remote state with S3

terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "production/terraform.tfstate"
    region         = "eu-west-3"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}
Enter fullscreen mode Exit fullscreen mode

5. CI/CD via GitHub Actions

- name: Terraform Apply
  run: terraform apply tfplan
  env:
    AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
    AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
Enter fullscreen mode Exit fullscreen mode

Cost Reference 2026

Stack Monthly Cost Best For
Cloud Run (GCP) $0-5 MVP, < 1K users
Vercel + Cloudflare $0 Static/Next.js
EC2 t3.micro + RDS $15-30 Up to 1K users
EC2 t3.small + RDS $45-60 Up to 5K users
ECS Fargate $40-80 Auto-scaling prod

Start with Cloud Run or Vercel. Move to EC2 when you have paying users.


The complete Terraform Starter Kit for Indie Hackers (all 5 templates with full code, CI/CD pipelines, cost optimization guide, and 30-min quickstart) is available at guittet.gumroad.com.

What stack are you running your indie project on? Drop it below 👇

Top comments (0)