Skip to content

Commit c501256

Browse files
committed
docs: add Local Development howto page
- Comprehensive howto/local-dev.md with all 20 profiles, ports, images, connection strings, Grafana dashboard, troubleshooting - Link from quickstart Step 7 to the new page - Updated FAQ custom metrics to use service/metrics/ interface pattern - Updated quickstart prompts with new cookiecutter variables
1 parent 7816186 commit c501256

2 files changed

Lines changed: 196 additions & 1 deletion

File tree

howto/local-dev.md

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
---
2+
layout: default
3+
title: "Local Development"
4+
parent: "How To"
5+
nav_order: 16
6+
description: "Docker Compose local dev stack with per-service profiles for databases, caches, message brokers, AWS/GCP emulators, and observability"
7+
---
8+
## Table of contents
9+
{: .no_toc .text-delta }
10+
11+
1. TOC
12+
{:toc}
13+
14+
## Overview
15+
16+
Projects generated from the [ColdBrew cookiecutter] include a `docker-compose.local.yml` with 20 infrastructure services, each behind its own [Docker Compose profile](https://docs.docker.com/compose/how-tos/profiles/). You select which services to start — only those containers run.
17+
18+
Your app runs natively via `make run` (fast builds, no Docker overhead). The compose stack provides only infrastructure dependencies.
19+
20+
## Quick Start
21+
22+
```bash
23+
make local-stack # start default services (selected during generation)
24+
make local-stack-obs # add Prometheus, Grafana, Jaeger
25+
make run # start the app
26+
make loadtest # generate traffic (ghz load test)
27+
```
28+
29+
Open [http://localhost:3000](http://localhost:3000) (Grafana) and [http://localhost:16686](http://localhost:16686) (Jaeger) to see metrics and traces.
30+
31+
## Available Profiles
32+
33+
During project generation, you choose default services via the `local_services` prompt. Override anytime with `PROFILES=`:
34+
35+
```bash
36+
make local-stack PROFILES="postgres kafka nats"
37+
```
38+
39+
### Databases
40+
41+
| Profile | Image | Host Port | Notes |
42+
|---------|-------|-----------|-------|
43+
| `postgres` | `postgres:18-alpine` | 5433 | Health check: `pg_isready` |
44+
| `mysql` | `mysql:8` | 3306 | Health check: `mysqladmin ping` |
45+
| `cockroachdb` | `cockroachdb/cockroach` | 26257 | UI on 8081, single-node insecure |
46+
| `mongodb` | `mongo:7` | 27017 | |
47+
| `alloydb` | `google/alloydbomni` | 5434 | GCP PostgreSQL-compatible |
48+
49+
### Cache
50+
51+
| Profile | Image | Host Port | Notes |
52+
|---------|-------|-----------|-------|
53+
| `redis` | `redis:8-alpine` | 6379 | Health check: `redis-cli ping` |
54+
| `valkey` | `valkey/valkey:8-alpine` | 6380 | Redis-compatible fork |
55+
| `memcached` | `memcached:alpine` | 11211 | |
56+
57+
### Messaging
58+
59+
| Profile | Image | Host Port | Notes |
60+
|---------|-------|-----------|-------|
61+
| `kafka` | `apache/kafka` | 9092 | KRaft mode (no Zookeeper) |
62+
| `nats` | `nats:alpine` | 4222 | JetStream enabled, monitoring on 8222 |
63+
64+
### Search
65+
66+
| Profile | Image | Host Port | Notes |
67+
|---------|-------|-----------|-------|
68+
| `elasticsearch` | `elasticsearch:8.17.0` | 9200 | Single-node, security disabled, health check |
69+
70+
### AWS Emulators
71+
72+
| Profile | Image | Host Port | Notes |
73+
|---------|-------|-----------|-------|
74+
| `ministack` | `nahuelnucera/ministack` | 4566 | Free LocalStack replacement (MIT), S3/SQS/SNS/DynamoDB |
75+
| `dynamodb` | `amazon/dynamodb-local` | 8000 | DynamoDB only |
76+
77+
### GCP Emulators
78+
79+
| Profile | Image | Host Port | Notes |
80+
|---------|-------|-----------|-------|
81+
| `spanner` | `gcr.io/cloud-spanner-emulator/emulator` | 9010/9020 | gRPC + REST |
82+
| `pubsub` | `google-cloud-cli:emulators` | 8085 | |
83+
| `bigtable` | `google-cloud-cli:emulators` | 8086 | |
84+
| `firestore` | `google-cloud-cli:emulators` | 8080 | |
85+
86+
### Tools
87+
88+
| Profile | Image | Host Port | Notes |
89+
|---------|-------|-----------|-------|
90+
| `adminer` | `adminer` | 8088 | SQL database admin UI |
91+
92+
### Observability (`obs`)
93+
94+
The `obs` profile starts all three observability services together:
95+
96+
| Service | Host Port | Notes |
97+
|---------|-----------|-------|
98+
| Prometheus | 9100 | Scrapes app on `host.docker.internal:9091` |
99+
| Grafana | 3000 | Pre-built ColdBrew dashboard (admin/admin) |
100+
| Jaeger | 16686 | OTLP receiver on 4317, traces flow automatically via `OTLP_ENDPOINT` in `local.env` |
101+
102+
## Makefile Targets
103+
104+
| Target | Description |
105+
|--------|-------------|
106+
| `make local-stack` | Start default profiles |
107+
| `make local-stack-obs` | Start default profiles + observability |
108+
| `make local-stack-down` | Stop all running containers |
109+
| `make local-stack-logs` | Follow container logs |
110+
| `make local-stack-reset` | Stop + restart |
111+
| `make local-exec SVC=<name> CMD="..."` | Exec into any running service container |
112+
| `make loadtest` | Run 10s gRPC load test with [ghz](https://ghz.sh) |
113+
114+
## Connecting Your App to Services
115+
116+
Your app runs on the host, not in Docker. Use `localhost:<host_port>` in your configuration:
117+
118+
```bash
119+
# Example local.env for Postgres + Redis + OTLP tracing
120+
ENVIRONMENT="dev"
121+
DATABASE_URL=postgres://postgres:postgres@localhost:5433/myapp_dev?sslmode=disable
122+
REDIS_URL=localhost:6379
123+
OTLP_ENDPOINT=localhost:4317
124+
OTLP_INSECURE=true
125+
```
126+
127+
## Customizing the Stack
128+
129+
### Adding a new service
130+
131+
1. Add the service to `docker-compose.local.yml` with a profile:
132+
```yaml
133+
myservice:
134+
image: myimage:latest
135+
profiles: ["myservice"]
136+
ports:
137+
- "8888:8888"
138+
```
139+
2. Run with `make local-stack PROFILES="postgres myservice"`
140+
141+
### Changing default profiles
142+
143+
Edit the `PROFILES` line in your Makefile:
144+
145+
```makefile
146+
PROFILES ?= postgres redis kafka
147+
```
148+
149+
Or override at runtime without editing the file:
150+
151+
```bash
152+
make local-stack PROFILES="postgres mongodb nats"
153+
```
154+
155+
### Disabling docker-compose entirely
156+
157+
During project generation, set `include_docker_compose` to `n`. The post-gen hook removes `docker-compose.local.yml` and `deploy/`. Makefile targets remain but are inert (docker-compose errors clearly when the file is missing).
158+
159+
## Grafana Dashboard
160+
161+
The `obs` profile auto-provisions a ColdBrew dashboard with:
162+
163+
- **Row 1 — RED Overview**: Request rate (QPS), error rate (%), latency p50/p95/p99
164+
- **Row 2 — gRPC Details**: Status code distribution, p95 latency by method
165+
- **Row 3 — Go Runtime**: Goroutines, heap usage, GC pause duration
166+
167+
The dashboard JSON is at `deploy/local/grafana/dashboards/coldbrew-service.json`. Edit it directly or modify through the Grafana UI (changes persist until `make local-stack-reset`).
168+
169+
## Troubleshooting
170+
171+
### Linux: Prometheus can't scrape the app
172+
173+
Prometheus runs in Docker and needs to reach the app on the host. The compose file includes `extra_hosts: host.docker.internal:host-gateway` which works on Docker Engine 20.10+. If scraping still fails, check `docker compose logs prometheus` for connection errors.
174+
175+
### Port conflicts
176+
177+
If a port is already in use, edit the host port mapping in `docker-compose.local.yml`:
178+
179+
```yaml
180+
ports:
181+
- "5434:5432" # change 5433 to 5434
182+
```
183+
184+
### `make local-stack-down` doesn't stop containers
185+
186+
The down command uses `--profile "*"` to see all profiled services. If containers persist, use `docker compose -f docker-compose.local.yml --profile "*" down --remove-orphans`.
187+
188+
### Grafana shows "No data"
189+
190+
1. Verify Prometheus is scraping: open [http://localhost:9100/targets](http://localhost:9100/targets) — your app should show as UP
191+
2. Verify the app is running (`make run`)
192+
3. Generate traffic: `make loadtest` or `curl localhost:9091/api/v1/example/echo -d '{"msg":"hello"}'`
193+
194+
---
195+
[ColdBrew cookiecutter]: /getting-started

quickstart.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ make local-exec SVC=postgres CMD="psql -U postgres" # Exec into any service
314314
Open [http://localhost:3000](http://localhost:3000) (Grafana, admin/admin) and [http://localhost:16686](http://localhost:16686) (Jaeger) to see metrics and traces in real-time.
315315

316316
{: .note }
317-
The local stack is infra-only — your app runs natively via `make run` for fast iteration. Use `make local-stack-down` to stop everything.
317+
The local stack is infra-only — your app runs natively via `make run` for fast iteration. Use `make local-stack-down` to stop everything. See the [Local Development How-To](/howto/local-dev/) for all 20 profiles, connection strings, Grafana dashboard customization, and troubleshooting.
318318

319319
## Step 8: Run in Docker
320320

0 commit comments

Comments
 (0)