Control which services run
Start optional services on demand, keep a published port, and give a service its own hostname.
Let a service declare its own hostname
Put a label on the service in its own compose file. The key names the host, the value is the container port:
services: app-dev: labels: multiverse.host.app: "4200" # app-u3.<domain> -> port 4200
With no labels at all, a service that exposes exactly one port is already reached at the app's own name. See Labels and variables for entrypoints and aliases.
Keep a service's published port
Universes drop every published host port, so two copies never collide. A service that genuinely needs one asks to keep it:
services: db: ports: ["5432:5432"] labels: multiverse.publish: "true"
Or, without touching the repository, list it in the manifest: publish: [db] on the app. Only one universe can hold that host port at a time.
Run optional services on demand
# multiverse.yaml, on the app optional: - name: celery services: [worker, beat]
$ multiversectl up 3 api --with celeryThe group name is also activated as a compose profile. Comma-separate several: --with celery,mail.
Configure what depends on the universe
A universe's hostnames do not exist until it does, so anything built from them — trusted origins, allowed hosts, CORS, a front-end's API base URL — is asked for with a label on the service that needs it:
services: web: labels: multiverse.env.DJANGO_ALLOWED_HOSTS: hosts multiverse.env.DJANGO_CSRF_TRUSTED_ORIGINS: origins multiverse.env.API_BASE_URL: url:devapi
The sources are origins (every address as an origin, each with its entrypoint's port), hosts, domain, universe, slug, host:<key> and url:<key>. An unknown one is an error naming the valid ones, never a variable that is quietly empty. They are written into the fragment generated for the app, which is merged last, so they win over the service's own environment: and over any env_file: — which is how a static list in a committed .env stops being what the universe runs with.
It is a label rather than a ${MULTIVERSE_ORIGINS} reference because a compose file is also run by hand: the reference would resolve to the empty string when multiversectl is not driving, while a label is inert metadata until it is. For a repository you cannot change, an app's inline overlay: does the same from the manifest:
apps: - name: api repo: api overlay: services: web: environment: DJANGO_CSRF_TRUSTED_ORIGINS: "${MULTIVERSE_ORIGINS}" DJANGO_ALLOWED_HOSTS: "${MULTIVERSE_HOSTS}"
Prefer the aggregates to listing hostnames one at a time: they follow the manifest, so adding a host key or an app leaves no settings list behind. There is also <HOST>_HOST and <HOST>_URL per host key, <REPO>_DIR per checkout, and UNIVERSE, UNIVERSE_SLUG, UNIVERSE_DIR, MULTIVERSE_DOMAIN, COMPOSE_PROJECT_NAME, HOST_UID, HOST_GID. multiversectl env <universe> prints the lot.