Skip to main content
In a Talos project the main app/API and each microservice are independent processes. They do not share a runtime. They communicate over HTTP, each listening on its own port, and the API finds a microservice by resolving a URL it reads from configuration. The 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
The 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
Set 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
The 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 its app.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 each microservices.<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.