devstack · Guide › Shared services & host access
Shared services are the whole point of devstack: one warm Postgres/Redis/MinIO (and friends) shared by every project in the workspace, instead of a duplicate stack per repo. This page covers what they are, how projects reach them, how they're reclaimed, and how to reach them from host tools (a GUI like DataGrip) when you need to.
devstack shared status # who's using what, and the ref counts
devstack shared expose # publish stable 127.0.0.1 ports for GUI clients
devstack shared ports # show those ports + ready-to-paste connection stringsYou declare shared engines once, in workspace.yaml:
apiVersion: devstack/v1
kind: Workspace
name: acme
shared:
postgres:
template: postgres
params: { version: "16" }
redis: { template: redis }
minio: { template: minio }At up, devstack starts these on a tool-owned external bridge network,
devstack_shared. Every project stack joins the same network, so a project's
containers reach the shared engines directly — no host ports involved.
See projects.md for how a service opts into a shared engine with
uses: / env.import, and templates.md for the engine catalog.
Inside the devstack_shared network, shared engines answer on stable DNS
aliases, never the bare service name (that avoids collisions with a project's
own postgres service):
| Engine | DNS alias | Port |
|---|---|---|
| Postgres | shared-postgres |
5432 |
| Redis | shared-redis |
6379 |
| MinIO | shared-minio |
9000 |
So a container connects to shared-postgres:5432, shared-redis:6379,
shared-minio:9000. There are no published host ports by default — the
network is the contract. This is why you don't get port clashes when three repos
are up at once.
The cleanest way to wire a service is to import the engine's exported attributes rather than hard-code the alias (see environment.md):
services:
api:
template: php.laravel.nginx
uses:
- workspace.shared.postgres
env:
import:
- { from: workspace.shared.postgres, vars: [host, port, user, password, database] }devstack keeps a ledger (keyed by Docker context) counting how many project
stacks currently reference each shared engine. up increments; down releases.
An engine at zero references is a candidate for reclamation — but it is never
stopped implicitly (there is no daemon, no autostop).
devstack shared statusShows each shared instance, its (engine, major) identity, ref count, and the
projects consuming it. Read-only; safe to run any time. Add --json to script it.
If the count ever drifts (a container was killed out-of-band), reconcile it:
devstack shared doctor # reconcile the ledger against live containers, prune dead refsdevstack shared gc # REPORT shared services sitting at zero references
devstack shared gc --stop # actually stop the zero-ref services| Flag | Default | Description |
|---|---|---|
--stop |
false | actually stop the zero-ref services (default is report-only) |
gc never removes volumes or data — a stopped engine's Postgres volume, buckets
and keys all survive the next up. See recovery.md for the wider
housekeeping story (doctor --fix, workspace destroy, uninstall).
By default the engines are DNS-only, which is great for containers but useless
for a GUI running on your host (DataGrip, TablePlus, a Redis browser, the MinIO
console). shared expose opts in to stable, loopback-only host ports:
devstack shared expose # publish every exposable shared service on 127.0.0.1
devstack shared expose postgres # scope it to one (or more) engines
devstack shared expose --off # remove the publish, back to DNS-only| Flag | Default | Description |
|---|---|---|
--off |
false | remove the host-port publish (back to DNS-only) |
Key properties:
- Loopback-only — ports bind
127.0.0.1, never0.0.0.0. - Stable — ports are ledger-allocated and reused across runs, so your saved
GUI connection keeps working. Bases sit in the 5xxxx range (e.g. Postgres →
55432) to avoid clashes. - Persistent — the publish is an up-time overlay that survives
up/down. It does not touch the deterministic generated compose. - Opt-in per service — with no arguments every exposable engine is published; name engines to narrow it.
Naming note. These are available both as top-level
devstack expose/devstack ports(the canonical spelling) and asdevstack shared expose/devstack shared ports(backward-compatible aliases). Use whichever you prefer — they run the same logic.
devstack shared portsRead-only projection of what's currently published, with a ready-to-paste connection string per port. No flags. Example output:
SERVICE PORT ADDRESS CONNECT
shared-postgres postgres 127.0.0.1:55432 postgres://devstack:devstack@127.0.0.1:55432/postgres?sslmode=disable
shared-redis redis 127.0.0.1:56379 redis://127.0.0.1:56379
shared-minio s3 127.0.0.1:59000 http://127.0.0.1:59000
shared-minio (console) console 127.0.0.1:59001 http://127.0.0.1:59001
per-project database: postgres://<project>:<project>@127.0.0.1:55432/<project>?sslmode=disable
The credentials shown for the top-level string are the shared engine's dev
admin creds (loopback-only threat model). Your per-project database has its
own role and password (both default to the project name) — connect with the
postgres://<project>:<project>@…/<project> DSN. See databases.md
for how those per-project roles and databases are created.
devstack up # engines running
devstack shared expose # publish loopback ports
devstack shared ports # copy the printed connection stringThen in DataGrip → New → Data Source → PostgreSQL, either paste the URL into the connection-string field, or fill in the parts:
| Field | Value |
|---|---|
| Host | 127.0.0.1 |
| Port | 55432 (from shared ports) |
| User | devstack (admin) or <project> |
| Password | devstack (admin) or <project> |
| Database | postgres (admin) or <project> |
For MinIO, point an S3 browser at http://127.0.0.1:59000 (API) and open the web
console at the console URL. For host-side AWS CLI calls against LocalStack/MinIO,
use devstack aws -- s3 ls instead of exposing ports — see
object-storage.md.
"I want to browse the dev DB in a GUI right now."
devstack shared expose postgres
devstack shared ports # paste the postgres:// string into your client"Free up RAM — stop engines nothing is using."
devstack shared status # confirm the ref counts are zero
devstack shared gc --stop"Ref counts look wrong after a crash."
devstack shared doctor # reconcile ledger ↔ live containers"Go back to DNS-only (no host ports)."
devstack shared expose --off- Lifecycle —
up/down/statusdrive the ref counts. - Databases — per-project Postgres roles, databases and grants.
- Object storage — tenant buckets on the shared MinIO.
- Recovery, teardown & housekeeping —
doctor --fix,workspace destroy. - Command reference — every command, terse.
◀ Lifecycle · Guide index · Databases ▶