microservice:create generator wires this up for you: it assigns the new service a free port, declares it in modules/app/app.yml, and adds an entry to the project root .env.yml so you can supply the real URL per environment.
Discovery is env-var-driven. The API never hard-codes where a microservice lives. Instead, app.yml names the environment variable that holds the URL, and the actual value comes from .env.yml for the current environment. Swap the value per environment (local, staging, production) without touching code.
How it works
The main app/API runs on port
8030. When you scaffold a microservice, the generator scans every modules/*/.env.yml for port: values already in use and picks the next free port starting at 8030, so each microservice gets its own distinct port and no two services collide. That chosen port is written to the new microservice’s own .env.yml under app.port.
Service declaration in app.yml
So the API can reach a microservice, the generator declares it in modules/app/app.yml under a microservices: list. Each entry pairs a name with the name of the environment variable that holds the service’s URL:
modules/app/app.yml
url value is not a URL. It is the name of the environment variable (pattern MICROSERVICE_<SNAKE_UPPER>_URL) that holds the actual URL. This indirection is what makes discovery environment-aware: the same declaration resolves to a different address in local, staging, and production.
Root .env.yml URL map
The generator also adds the microservice to the project root .env.yml under a microservices: map. This is where you fill in the real URL for each environment:
.env.yml
url to the address where the service is reachable in that environment: http://localhost:8030 locally, or the deployed hostname in staging and production. The declaration in app.yml reads this value through its named environment variable.
Per-microservice runtime config
Each microservice carries its own runtime configuration in its own.env.yml: its distinct port plus its own datastores. A service does not share the API’s Redis or Postgres; it gets its own:
modules/billing/.env.yml
app.port here is the port the generator assigned. The Redis URLs (cache, pubsub, rate_limit) and the Postgres database.url are the service’s own backing stores; keep them separate so each service owns its data.
Talking between services
Communication is over HTTP at the configured URL. The API resolves a microservice’s address from itsapp.yml declaration (the named env var) and the value supplied in .env.yml, then issues an HTTP request to that address. Use the framework’s HTTP client utility (@talosjs/fetcher) to make the call; it is the standard way to perform HTTP requests in a Talos app.
Because the address always comes from configuration, you never embed a hostname in code. Change the environment, change the URL, and the calling service follows.
Wiring services without surprises
Fill in eachmicroservices.<name>.url in .env.yml for local, staging, and production, and inject production values through your platform’s secrets/env rather than committed files. Never hard-code a URL in source: always go through the env-var-named declaration in app.yml so the address is resolved from configuration. The same rule applies to ports. Let the generator pick them; it scans modules/*/.env.yml and assigns the next free port from 8030 so nothing collides.
Treat every microservice as a remote process, not a local import. Resolve its address from config and call it with the HTTP client. Each service owns its own Redis and Postgres (cache, pubsub, rate_limit, database), so don’t point two services at the same database for shared state; talk over HTTP instead. When a service is retired, microservice:remove strips its entry from modules/app/app.yml, the root .env.yml map, and its docker-compose block, so let the generator clean up rather than editing those files by hand.
Related
- Microservice overview: what microservices are in a Talos project and when to reach for one.
- Microservice structure: how a generated microservice is laid out.
- microservice:create: the generator that assigns the port and writes these config entries.
- Configuration: how
.env.ymlandapp.ymlare loaded and merged per environment. - Fetcher: the HTTP client utility for calling services at their configured URLs.
- Deploy a microservice: supplying per-environment URLs and secrets when you ship.