Define Process Ownership and Port Mapping

Define Process Ownership and Port MappingMost teams’ local development environments look like a car pile-up. Multiple services spinning up, port conflicts everywhere, developers stuck for hours figuring out whether the problem lives in the database, the queue worker, or just some forgotten process from yesterday. We’ve watched this happen enough times to know it stems from one thing: nobody quite owns anything.

The fix isn’t complicated, but it requires discipline. Each service needs to own exactly one thing. Your web server handles HTTP. Your queue worker handles jobs. Your database handles data. The moment someone decides “oh, the queue worker should probably manage database connections,” you’ve created a hidden dependency that won’t bite you until it breaks in production.

Treat Each Service as an Independently Managed Process

Docker Compose makes this enforcement natural because each service sits in its own container. Use it. Configure the container name, the image version, environment variables through a .env file, health checks so you know it’s actually running, dependency ordering with depends_on, and volume mounts for local work.

Yes, this takes an hour to set up properly. It saves ten hours later.

Make Ports and Dependencies Explicit

Document every port and dependency in your compose.yaml or docker-compose.yml. No guessing. No “I think it runs on 3000.” Docker’s documentation describes Compose networks as the path for service-to-service communication, while published ports create access from the host machine. Those are different jobs, and keeping them separate makes a local system easier to reason about.

A development setup might say: PostgreSQL on 5432 internally, exposed to the host on 5433 when a local database client is needed. Redis on 6379, internal only. A web API on 3000 internally, mapped to 8000 on the host. A worker with no exposed ports, depending on PostgreSQL and Redis.

Suddenly the onboarding story changes. A new developer reads the file, runs one command, and understands the system instead of spending forty minutes asking which tab starts what.

Use Internal Networking by Default

Inside a Compose network, services can call one another by service name, such as postgres:5432, rather than assuming that localhost means the same thing from every process. Docker documents this service-name pattern as part of Compose networking.

That distinction matters. The API container and the developer’s Mac do not share the same network namespace. A database connection that works from one place can fail from the other without the database being broken.

Expose only what needs to be debugged from the host. The API usually needs a browser-facing port. PostgreSQL may need one for a local query tool. Cache and queue services often do not. Fewer published ports means fewer collisions and a clearer boundary between human-facing services and machine-to-machine dependencies.

Give Failure Modes Somewhere to Go

Here’s what we look for now: which process owns the symptom, and what evidence would prove it?

If the database is slow, start with database activity, query behaviour, and connection state. PostgreSQL’s monitoring documentation describes its cumulative statistics system for inspecting server activity, and its logging documentation covers outputs including stderr, CSV, JSON, and syslog.

If jobs are waiting, inspect the worker and the queue. Laravel’s queue documentation separates the queue:work process, worker signals, timeouts, supervisor configuration, and failed jobs for a reason: “the worker is running” is not the same as “the worker is processing useful work.”

If a port is already taken, inspect the process holding that port and the host mapping. Do not start by rewriting application code. If configuration differs between machines, inspect the environment first. Node’s documentation treats environment variables as process input, while the Twelve-Factor App recommends keeping deploy-specific configuration in the environment rather than in code.

This is less exciting than hunting for a clever bug. It is also usually faster.

Version the Setup Like Code

Keep the Compose file in the repository. Review changes in pull requests. Add a .env.example that lists every required variable with safe, useful defaults. Include the commands that start the web process, worker, database, and any supporting services.

The Twelve-Factor App also makes a useful point about development and production parity: the closer the environments are, the smaller the gap where environment-specific bugs can hide. Local development will never be production, but it should not be an entirely different species either.

Logs deserve the same treatment. The Twelve-Factor guidance describes logs as event streams rather than files that an application must own forever. Docker Compose provides docker compose logs to view service output. The practical goal is simple: when something fails, the relevant process should leave evidence somewhere predictable.

Where a Control Plane Helps

At a certain point, a project has enough processes that a collection of terminal tabs stops being a useful interface. Laravel, Vite, a queue worker, PostgreSQL, Redis, Docker, and a local API can all be healthy while the overall project is still unusable because one dependency is stale or pointed at the wrong port.

A local control plane helps by making process ownership, state, ports, and logs visible together. It should not hide the underlying commands or make developers learn a new abstraction before they can debug. The useful layer is the one that removes repetition while preserving the evidence.

That is the standard we use for Rig: one window for every process in local development, with the system state close enough to inspect when an AI assistant or a human developer needs context. The point is not another dashboard. The point is fewer invisible gaps between “running” and “working.”

A Small Audit With a Large Payoff

Start with the project you are opening today.

Can every service state what it owns? Are internal ports different from host ports where they need to be? Does the Compose file show dependencies clearly? Can a new developer find the required environment variables and startup command without asking someone? When a worker stops making progress, is there a heartbeat, log line, or queue metric that makes that visible?

If the answer is no, spend the next couple of hours documenting the setup. It is boring work. Boring architecture is what lets the interesting work ship.

https://userig.app/blog/define-process-ownership-and-port-mapping