This project is a platform-engineered microservices starter template for building distributed systems with Rust. It is built for engineering teams that care about platform integrity and wish(as much as possible) to avoid integrating external/third-party tooling for their distributed system builds.
The base is intentionally minimal, with two services:
mesh- a registry and control plane for service registration, heartbeat refresh, load-balanced service discovery, and more. It is adapted from Rusty Mesh.auth- a PostgreSQL-backed auth/session/RBAC service, adapted from Rusty Auth.
- System Shape
- Requirements
- Quick Start
- Runtime URLs
- How Service Discovery Works
- Configuration
- Common Operations
- Development Workflow
- CI
- Security Notes
- Troubleshooting
- Repository Layout
The root Compose stack runs the project as one unit:
Host
|-- 127.0.0.1:3080 -> mesh
|-- 127.0.0.1:8000 -> auth
`-- 127.0.0.1:5433 -> auth-db
Compose network
|-- mesh -> service registry/control-plane
|-- auth -> auth microservice; registers into mesh
`-- auth-db -> PostgreSQL for auth
The mesh owns service registration, heartbeat refresh, discovery, and endpoint metadata. The
auth service on the other hand, starts after PostgreSQL is healthy, registers itself with the
mesh, refreshes its lease by heartbeat, and unregisters during graceful shutdown.
Mesh endpoint resolution for development is intentionally configured as:
APP__REGISTRY__EXTERNAL_ENDPOINT_RESOLUTION=docker
That means Mesh inspects Docker to resolve the host port published for a registering container. The
root Compose file mounts /var/run/docker.sock into Mesh read-only for that purpose. Learn more
about this by reading the rusty-mesh documentation.
- Docker with the Compose plugin
- Rust, for local service development outside Docker
- Bun, for project root static-analysis tooling
sqlx-cli, only when running auth migrations manually outside Compose
The fastest path to a complete project start-up is Docker Compose. For that, Rust and the sqlx-cli
are not required.
Copy the root environment sample:
cp .env.sample .envStart the full stack:
docker compose up -d --buildFor faster Compose builds on newer Docker setups, you can enable Bake:
COMPOSE_BAKE=true docker compose up -d --buildThe first build can take several minutes because the Rust services compile their release dependencies inside Docker. Later builds should reuse Docker and Cargo build cache layers.
Check running containers:
docker compose psCheck Mesh health:
curl http://127.0.0.1:3080/api/v1/mesh/healthList services registered with Mesh:
curl -H "authorization: Bearer ${MESH_TOKEN:-local-dev-mesh-token}" \
http://127.0.0.1:3080/api/v1/mesh/servicesYou should see auth-service after Auth has started and registered.
If auth takes a little longer to appear, check its logs while it waits for PostgreSQL and
registers with the mesh:
docker compose logs -f authSmoke-test Auth by registering a disposable user:
curl -X POST http://127.0.0.1:8000/api/v1/auth/register \
-H "content-type: application/json" \
-d '{
"first_name": "Smoke",
"last_name": "User",
"email": "smoke@example.com",
"password": "password123",
"country": "Testland",
"country_code": "TL",
"phone_number": "1000000001"
}'Stop the stack:
docker compose downRemove the database volume when you want a clean database:
docker compose down -v| Service | URL | Notes |
|---|---|---|
| Mesh health | http://127.0.0.1:3080/api/v1/mesh/health |
Public health route |
| Mesh registry | http://127.0.0.1:3080/api/v1/mesh/services |
Requires mesh bearer token |
| Auth API | http://127.0.0.1:8000/api/v1/auth |
Public and protected auth routes |
| Auth database | 127.0.0.1:5433 |
Local host mapping to Postgres |
Auth is configured in root Compose with:
APP__MESH__ENABLED=true
APP__MESH__URL=http://mesh:3080
APP__MESH__SERVICE_NAME=auth-service
APP__MESH__SERVICE_VERSION=1.0.0
APP__MESH__ADVERTISE_HOST=auth
When Auth starts:
- Auth binds inside the container on port
8000. - Auth sends a registration request to Mesh over the Compose network.
- Auth includes its container id through
x-mesh-container-id. - Mesh inspects Docker because endpoint resolution is
docker. - Mesh stores both endpoint views:
- external host endpoint, for operator access from the host
- internal Compose-network endpoint, for service-to-service calls
- Auth refreshes the lease through heartbeat every
MESH_HEARTBEAT_INTERVAL_SECS.
The public registry response should include fields like:
{
"name": "auth-service",
"version": "1.0.0",
"ip": "127.0.0.1",
"port": 8000,
"internal_ip": "auth",
"internal_port": 8000,
"url": "http://127.0.0.1:8000"
}If a service should call another service inside the Compose network, discovery clients can request the internal endpoint by sending:
x-mesh-endpoint-scope: internalRoot runtime values live in .env.sample. Copy it to .env and edit the values for
your environment.
Important root variables:
| Variable | Default | Purpose |
|---|---|---|
MESH_HTTP_PORT |
3080 |
Host port for Mesh |
MESH_TOKEN |
local-dev-mesh-token |
Shared token for protected Mesh routes |
MESH_PUBLIC_HOST |
127.0.0.1 |
Host used for Docker-resolved external endpoints |
MESH_HEARTBEAT_INTERVAL_SECS |
5 |
Service heartbeat interval |
MESH_SERVICE_TTL_SECS |
15 |
Registry lease TTL |
AUTH_HTTP_PORT |
8000 |
Host port for Auth |
AUTH_JWT_SECRET |
change-me-before-production |
Auth JWT signing secret |
AUTH_POSTGRES_PORT |
5433 |
Host port for Auth PostgreSQL |
AUTH_POSTGRES_USER |
rusty_auth |
Auth DB user |
AUTH_POSTGRES_PASSWORD |
rusty_auth |
Auth DB password |
AUTH_POSTGRES_DB |
rusty_auth |
Auth DB name |
Each service also keeps its own standalone configuration and README:
Use the service READMEs when working inside a service directly. Use this root README when operating the full project as a composed system.
Build without starting:
docker compose buildStart in the foreground:
docker compose up --buildStart in the background:
docker compose up -d --buildStart in the background with Compose Bake enabled:
COMPOSE_BAKE=true docker compose up -d --buildView logs:
docker compose logs -f mesh
docker compose logs -f auth
docker compose logs -f auth-dbRestart one service:
docker compose restart authRebuild one service:
docker compose up -d --build authInspect the effective Compose configuration:
docker compose configOpen a Postgres shell:
docker compose exec auth-db psql -U "${AUTH_POSTGRES_USER:-rusty_auth}" \
-d "${AUTH_POSTGRES_DB:-rusty_auth}"Install root tooling:
bun installRoot scripts:
bun run format
bun run format:check
bun run services:check
bun run services:testservices:check discovers Rust services under microservices/*/Cargo.toml and runs:
cargo check --locked --all-targets --all-featurescargo fmt --all -- --checkcargo clippy --locked --all-targets --all-features -- -D warnings
services:test runs full tests for services that do not need a database. For services with
migrations, it always runs library tests and runs DB-backed controller tests when PostgreSQL is
reachable.
Local hooks are installed by Husky:
pre-commitvalidates shell hooks, Rust service quality checks, and repository formatting.pre-pushruns service tests and repository formatting checks.commit-msgenforces Conventional Commit messages.
GitHub Actions validates the root and each integrated service:
- root formatting and shell-hook validation
- Rust quality checks for Auth and Mesh
- full Mesh tests
- full Auth tests with PostgreSQL and SQLx migrations
- Docker image builds for Auth and Mesh
The root project is not a Rust workspace, so CI intentionally enters each service directory instead of running one Cargo command from the repository root.
Change these before any shared or production-like deployment:
MESH_TOKENAUTH_JWT_SECRETAUTH_POSTGRES_PASSWORD
Mesh registry routes are protected by the mesh token. The health route remains public so load balancers and operators can check liveness without holding internal credentials.
Docker endpoint resolution requires Docker socket access. Treat this as privileged control-plane access. The socket is mounted read-only in the Compose file, but the Docker Engine API can still expose sensitive runtime metadata. Keep this mode for trusted local or controlled deployments where Mesh is allowed to inspect service containers.
For stricter environments, switch Mesh endpoint resolution to none and have services register
explicit external endpoint fields instead.
Mesh registry returns 401:
- Confirm
MESH_TOKENin.env. - Send
Authorization: Bearer <token>to/api/v1/mesh/servicesroutes.
Auth does not appear in Mesh:
- Check
docker compose logs -f auth. - Check that
APP__MESH__ENABLED=truein the rendered Compose config. - Check that Auth can reach
http://mesh:3080inside the Compose network. - Confirm Mesh started with
APP__REGISTRY__EXTERNAL_ENDPOINT_RESOLUTION=docker.
Mesh shows internal endpoint instead of host endpoint:
- Confirm
/var/run/docker.sockis mounted into the Mesh container. - Confirm the registering service publishes its internal port through Compose.
- Confirm the service sends
x-mesh-container-id; Auth does this through its registry client.
Auth cannot connect to PostgreSQL:
- Check
docker compose ps auth-db. - Check
docker compose logs -f auth-db. - Confirm Auth DB environment variables match the Postgres service values.
- Reset the database volume if the existing volume was created with different credentials:
docker compose down -v
docker compose up -d --buildPort already in use:
- Change
MESH_HTTP_PORT,AUTH_HTTP_PORT, orAUTH_POSTGRES_PORTin.env. - Rerun
docker compose up -d.
.
|-- .github/ # CI, pull request template, issue templates
|-- .husky/ # local Git hooks
|-- compose.yaml # whole-project runtime stack
|-- microservices/
| |-- auth/ # Rusty Auth service
| `-- mesh/ # Rusty Mesh service
|-- scripts/ # root service-check orchestration
|-- .env.sample # root Compose environment sample
|-- CONTRIBUTING.md
|-- SECURITY.md
|-- CODE_OF_CONDUCT.md
|-- LICENSE
`-- README.md