dx is a project-agnostic Docker eXecutioner CLI — a Compose wrapper that simplifies container management for both development and production environments. It provides smart command routing between the host machine and running containers, making it ideal for monorepo setups — with or without Turborepo.
- Features
- Requirements
- Companion Scripts
- Installation
- Usage
- Setup
- Dev lifecycle
- Production lifecycle
- Status
- Runner commands
- Service shortcuts
- Configuration
- Compose file setup
- Monorepo usage
- .gitignore recommendation
- Full command reference
- Smart Proxy: Detects whether commands should run on the host or be forwarded to the runner container automatically.
- Dev & Prod Stacks: Separate lifecycle commands for development (
compose.dev.yaml) and production (compose.yaml) — prefix any command withpfor production. - Task Runner Detection: Automatically detects
npm,pnpm,yarn,bunormakebased on project files. - First-time Setup: Copies all
*.exampleenv files to their real counterparts with a single command. - Shell Shortcuts: Open an interactive shell or run a one-off command in any service container (dev stack).
- Runner Container: Proxies
npm run <script>calls through a dedicated runner container for consistent environments. - Safety Guards: Destructive commands (
downv,pdownv) require interactive confirmation. - Configurable: All defaults are overridable via environment variables — no need to edit the script.
- Docker Detection: Automatically detects when running inside a container and executes commands locally instead of re-entering Docker.
- Named Arguments: Consistent short and long flag support across all commands.
bashdockerwith the Compose plugin (docker compose)
The runner container used by dx run and dx exec must include:
bashgit(optional, forcodebase-dump.sh)postgresql-client(optional, fordb-dump.sh/db-restore.sh)perl(optional, for binary detection incodebase-dump.sh)docker-cli(optional, for Docker-in-Docker commands)
A minimal Dockerfile.runner example:
FROM node:22-alpine
RUN apk add --no-cache bash git postgresql-client perl docker-cli
WORKDIR /app
CMD ["bash"]To keep dx lightweight and focused, specialized tasks like database backups and codebase exports are maintained in separate repositories. You should place these scripts in the scripts/ directory (alongside Dockerfile.runner):
- db-dump: Smart PostgreSQL backup script (Docker-aware).
- db-restore: Database restoration script with schema reset support.
- codebase-dump: AI-ready project codebase exporter (respects
.gitignore).
Copy dx into your project root and make it executable:
chmod +x dxThat's it. No global install required.
bash dx <command> [args...]Or, after making it executable:
./dx <command> [args...]bash dx setupCopies all *.example files in the project to their real counterparts (e.g. .env.example → .env). Skips files that already exist. Run once after cloning, then fill in your values.
| Command | Description |
|---|---|
bash dx dev [args] |
Start all containers in detached mode |
bash dx devb [args] |
Start dev containers in detached mode with --build |
bash dx stop [svc] |
Stop running containers (keeps volumes) |
bash dx down [args] |
Stop and remove containers |
bash dx downv [args] |
Stop and remove containers + volumes (requires confirmation) |
bash dx restart [svc] |
Restart one or all services |
bash dx build [args] |
Build images |
bash dx rebuild [args] |
Build images without cache |
bash dx logs [svc] |
Follow log output (Ctrl-C to stop) |
bash dx shell <svc> [cmd...] |
Open an interactive shell or run a command in a service |
If the target container is not running, shell falls back to docker compose run --rm automatically.
Prefix any lifecycle command with p to target the production stack (compose.yaml):
| Command | Description |
|---|---|
bash dx prod [args] |
Start the production stack in detached mode |
bash dx prodb [args] |
Start the production stack in detached mode with --builds |
bash dx pstop [svc] |
Stop production containers |
bash dx pdown [args] |
Stop and remove production containers (requires confirmation) |
bash dx pdownv [args] |
Stop and remove production containers + volumes (requires confirmation) |
bash dx prestart [svc] |
Restart one or all production services |
bash dx pbuild [args] |
Build production images |
bash dx prebuild [args] |
Build production images without cache |
bash dx plogs [svc] |
Follow production log output |
bash dx pshell <svc> [cmd...] |
Open an interactive shell in a production container |
| Command | Description |
|---|---|
bash dx ps |
List containers with status and health |
These commands are routed through the runner container (or executed locally if already inside a container):
| Command | Description |
|---|---|
bash dx run <script> [args] |
Run an npm script or make target at the root |
bash dx exec <cmd> [args] |
Run any arbitrary command in the runner |
bash dx sh |
Open an interactive shell in the runner |
If the script definition in package.json contains dx (i.e. it itself manages Docker), dx run executes it on the host. Otherwise, it forwards to the runner container. This prevents unnecessary nesting.
Example package.json scripts:
{
"scripts": {
"test:api": "npm run test -w project-api",
"test:api:dx": "bash dx shell api npm run test",
"restore:db": "bash scripts/db-restore.sh -e .env",
"restore:db:dx": "bash scripts/db-restore.sh -e .env.docker",
}
}bash dx run test:api→ forwarded to runner containerbash dx run test:api:dx→ executed on host (containsdx)
Any unrecognized command is treated as a service name and opens a shell in the development stack:
bash dx api # equivalent to: bash dx shell api
bash dx web # equivalent to: bash dx shell web
bash dx postgres # equivalent to: bash dx shell postgresAll defaults can be overridden via environment variables — no need to edit the script:
| Variable | Default | Description |
|---|---|---|
DX_DEV_COMPOSE |
compose.dev.yaml |
Path to the dev Compose file |
DX_PROD_COMPOSE |
compose.yaml |
Path to the prod Compose file |
DX_RUNNER |
runner |
Runner service name |
DX_RUNNER_PROFILE |
tools |
Compose profile for the runner service |
Example:
DX_DEV_COMPOSE=docker/dev.yaml bash dx devThe runner service must be defined with the correct profile so it does not start automatically with dx dev:
services:
runner:
build:
context: .
dockerfile: scripts/Dockerfile.runner
working_dir: /app
profiles: ["tools"]
volumes:
- .:/app
- node_modules:/app/node_modules
env_file:
- apps/api/.env.docker
command: bashStandard Compose file — no special requirements. Docker eXecutioner uses it as-is for all p-prefixed commands.
Docker eXecutioner works independently of Turborepo. It relies only on your Compose files and the task runner it detects.
With Turborepo, your package.json scripts might look like:
{
"scripts": {
"dev": "turbo run dev",
"build": "turbo run build",
"test": "npm run test:api && npm run test:web",
"test:dx": "bash dx shell api npm run test && bash dx shell web npm run test"
}
}Without Turborepo, they work the same way — Docker eXecutioner only cares about the task runner (npm, pnpm, yarn, bun) and your Compose files.
# Env
.env
.env.local
.env.*.local
.env.docker
!.env.example
!**/.env.example
!**/.env.docker.example
# Dumps (e.g. database dumps, codebase dumps, etc.)
dumps/
bash dx help