Microservices as One Engineer (Go + AWS + Terraform)
Microservices for one engineer pay off when you have the right tooling — Go services, Terraform IaC, and AWS ECS Fargate. Here's how I make it work solo.
The common advice is "don't use microservices if you're a small team." I disagree — if your tooling is right, microservices for one engineer are manageable and often worth it. Here's the exact setup I use to run six products as microservices with no ops team.
When microservices make sense solo
The break-even point isn't team size — it's deployment independence and failure isolation. If LetX's compile service goes down, it shouldn't take the CRDT sync server with it. If QuantumSketch's Manim renderer is chewing CPU, it shouldn't affect API latency for other products.
For me: separate deployable units per concern, with a shared infrastructure layer.
The module structure
services/
├── letx-api/ # Go: auth, document CRUD, user management
├── letx-collab/ # Go: CRDT sync, WebSocket sessions
├── letx-compiler/ # Go+Docker: LaTeX compilation sandbox
├── quantumsketch-api/ # Go: prompt intake, job queue
├── quantumsketch-worker/ # Python: Manim render, TTS, ffmpeg
├── bikroybuddy-api/ # Go: WhatsApp webhook, search
├── context-heavy/ # Go: knowledge graph API
└── shared/
├── auth/ # JWT middleware (Go library)
├── db/ # pgx pool config
└── observability/ # OpenTelemetry setup
Shared libraries are internal Go modules — not microservices themselves, but packages imported by services. No RPC between shared libraries and services; they're compiled in.
Terraform: infrastructure as code
Every service is deployed via a shared Terraform module:
module "letx_collab" {
source = "../../modules/ecs-service"
name = "letx-collab"
image = "${var.ecr_base}/letx-collab:${var.image_tag}"
cpu = 256
memory = 512
port = 8080
desired_count = 2
environment = {
DB_DSN = var.db_dsn
REDIS_URL = var.redis_url
JWT_SECRET = var.jwt_secret
}
health_check_path = "/health"
alb_listener_arn = aws_alb_listener.main.arn
alb_priority = 10
}
Adding a new service is 10 lines of HCL. No console clicking. terraform plan shows exactly what changes before apply.
Service communication
I keep inter-service communication simple:
| Pattern | Used for | |---------|----------| | HTTP/JSON | Synchronous requests between services | | SQS | Async work queues (compile jobs, render jobs) | | WebSocket (direct) | Real-time CRDT sync (letx-collab only) | | Shared PostgreSQL | Read-heavy cross-service queries | | Redis pub/sub | Real-time notifications (lightweight) |
No gRPC. For a solo engineer, the complexity of proto files, codegen, and versioning is not worth the marginal performance gain over HTTP/JSON between services that all live in the same AWS region.
CI/CD per service
Each service has its own GitHub Actions workflow:
# .github/workflows/letx-collab.yml
on:
push:
paths:
- 'services/letx-collab/**'
- 'shared/**'
jobs:
deploy:
steps:
- name: Build and push Docker image
run: |
docker build -t $ECR_REPO:$SHA services/letx-collab/
docker push $ECR_REPO:$SHA
- name: Update ECS service
run: |
aws ecs update-service \
--cluster prod \
--service letx-collab \
--force-new-deployment
Path filtering means touching letx-collab/ only triggers letx-collab deployment. No global deploys from a single change.
Observability without an ops team
Three tools, all low-maintenance:
- AWS CloudWatch — ECS metrics (CPU, memory, task count). Free tier covers everything at my scale.
- OpenTelemetry → Grafana Cloud — distributed traces. Grafana Cloud free tier: 14-day retention, sufficient for debugging.
- Structured JSON logs — every service logs JSON to stdout. CloudWatch Logs Insights can query across all services.
// shared/observability/log.go
func Info(ctx context.Context, msg string, fields ...slog.Attr) {
slog.LogAttrs(ctx, slog.LevelInfo, msg,
append(fields,
slog.String("service", ServiceName),
slog.String("trace_id", TraceID(ctx)),
)...)
}
What breaks at solo-scale
Database connections. 8 services × 10 connection pool size = 80 connections minimum. RDS db.t3.small maxes at 40. Fix: pgBouncer in transaction mode, or use RDS Proxy.
Terraform state conflicts. Two fast deploys of different services can conflict on the Terraform state lock. Fix: separate Terraform state per service using S3 backend with DynamoDB locking.
Runaway ECS tasks. A bug that causes task crash-looping triggers ECS to spin up replacement tasks — burning CPU and costing money. Fix: CloudWatch alarm on ECS CrashCount > 3 → auto-stop the service + notify Slack.
FAQ
Are microservices worth it for a solo developer? Yes, if you have the right tooling (Terraform, ECS, GitHub Actions) and your services have genuine deployment independence. Without tooling, microservices are overhead; with it, they provide valuable failure isolation.
What's the alternative to microservices for solo founders? A well-structured monolith with clear module boundaries is the right choice for most solo founders starting out. Migrate to microservices when deployment independence or failure isolation becomes a real pain point.
How do you handle service discovery? AWS ALB routes by hostname (letx-api.internal, letx-collab.internal). Services call each other by internal hostname. No service mesh needed at this scale.
What's the minimum AWS spend for this setup? ~$80–120/month for a production microservices setup on ECS Fargate with RDS PostgreSQL (db.t3.micro) and ALB.
Written by Shihab Shahriar Antor — AI Engineer & Founder of Shahriar Labs. See also: Terraform on AWS: Infrastructure as Code Guide · The Solo Founder Stack.