Installation
Start the application with PostgreSQL and Redis, confirm that it is ready, then choose your next feature. You do not need a cloud account, Kubernetes, or email credentials for this first run.
Want to understand a module first? Run go test ./tests/modules -run TestFirstModule
and follow your first module. That example runs entirely in process.
This tutorial runs the template as supplied. To make it your own application, use the installer to set its name and module path. Then follow adoption and updates to incorporate future security fixes.
Requirements#
- The Go version declared in
go.mod(go versionshows your installed version). - Docker for the two disposable services below.
- Two terminals, opened at the repository root.
The installed application uses PostgreSQL, Redis-backed Asynq tasks, and process-local Watermill events. Redis persists tasks; local events do not persist.
Start local dependencies#
These containers use dedicated host ports and disposable data. The credentials below are tutorial values only:
docker run --rm -d --name go-modular-quickstart-postgres \
-e POSTGRES_USER=demo -e POSTGRES_PASSWORD=local-demo \
-e POSTGRES_DB=go_modular -p 127.0.0.1:55432:5432 postgres:17-alpine
docker run --rm -d --name go-modular-quickstart-redis \
-p 127.0.0.1:56379:6379 redis:7-alpine
Check both services before starting the application:
docker exec go-modular-quickstart-postgres pg_isready -U demo -d go_modular
docker exec go-modular-quickstart-redis redis-cli ping
Expect accepting connections and PONG. PostgreSQL may need a few seconds;
repeat the readiness check if it is still starting. For retained data, see
local development.
Configure and migrate#
Select the supplied tutorial profile in the first terminal:
export APP_ENV_FILE=.env.quickstart.example
go run ./cmd/cli migrate up
This profile points to the two containers above. It disables authentication, cache, storage, push, scheduling and telemetry. Mail is always constructed, but SMTP connects only when sending: this tutorial sends no email.
migrate up applies the registered Goose migrations and returns to your prompt.
Migrations are explicit; starting the server never changes the schema. Confirm
that there are no pending migrations:
go run ./cmd/cli migrate status
Existing shell variables override the profile. An unexpected database, credential error or exporter usually means an inherited override; review configuration precedence.
Start the application#
In the same terminal:
go run ./cmd/app
The app main explicitly starts HTTP, queue workers, local event subscribers and the notification dispatcher. Run cmd/scheduler separately when recurring maintenance is needed. Leave this terminal running.
Check the app and enable signup#
In the second terminal:
curl -i http://127.0.0.1:8080/healthz
curl -i http://127.0.0.1:8080/readyz
Both return 200 after successful startup. /healthz checks the HTTP listener;
/readyz checks application startup/shutdown state. Dependency probes are a
separate readiness option.
Open the running API documentation, or inspect its current schema:
curl http://127.0.0.1:8080/openapi.json
These routes reflect the registered application immediately. No OpenAPI generation command is required after editing a route.
The quickstart has authentication disabled, so protected routes intentionally
reject requests. To try signup/sign-in, copy the tutorial profile into your own
local file, configure SMTP and Identity keys, and follow
Identity setup. Do not use
.env.example without filling its enabled capabilities' credentials.
| Ready to try… | Continue with… |
|---|---|
| An endpoint you can change | Your first module |
| Email OTP and social sign-in | Identity |
| A stored account through HTTP and CLI | Persisted operation walkthrough |
| A background task | Queues |
| The full container stack | Local development |
Run a console command#
Select the same profile in the second terminal:
export APP_ENV_FILE=.env.quickstart.example
go run ./cmd/cli help
go run ./cmd/cli route list
Help lists nested command paths. route list prints methods, paths and operation
names without opening another HTTP listener. Ordinary CLI commands construct and
connect the application but do not run workers, subscribers or schedules.
See console commands for the complete execution model.
Follow the request through the code#
When reading the supplied Identity module, follow this sequence:
| Responsibility | Location |
|---|---|
| Register an operation and its schema | internal/modules/identity/delivery/http/routes.go |
| Decode input and map service arguments | internal/modules/identity/delivery/http/requests.go |
| Authorize and call the operation | internal/modules/identity/delivery/http/*_handler.go |
| Apply business rules | internal/modules/identity/service |
| Execute queries | internal/modules/identity/repository |
| Define the user and its invariants | internal/modules/shared/entities |
| Construct and connect the module | internal/modules/identity/register.go, internal/app/modules.go |
The directory map and dependency guide explain why these pieces are separate and where shared capabilities fit.
Stop and verify#
Press Ctrl+C in the application terminal. The application drains admitted work within its shutdown budget, then closes owned resources. Remove the tutorial containers when finished; this deletes their disposable data:
docker rm -f go-modular-quickstart-postgres go-modular-quickstart-redis
For a quick source check:
go test ./tests/modules -run TestFirstModule -count=1
go vet ./...
Use the testing guide for the full suite and disposable integration services. Tests that skip because their service settings are absent have not verified those integrations.
If the first run fails#
| Symptom | First check |
|---|---|
| Docker reports a name already in use | A previous tutorial container is still running |
| Database connection refused | PostgreSQL readiness and host port 55432 |
| Redis connection refused | Redis readiness and host port 56379 |
| Credentials or unexpected services are required | APP_ENV_FILE and inherited process variables |
| HTTP address is already in use | Another app, Compose or Tilt process owns port 8080 |
| Protected endpoint returns an error | Authentication is intentionally off in this tutorial |
Continue with configuration, then your first module. More detailed symptoms are covered in troubleshooting.