missbetty
v1.5.2
Published
Local Docker development domains through one Traefik switchboard
Maintainers
Readme
Betty
Betty is a lightweight CLI for local Docker development domains. It links running Docker containers to local domains through one global Traefik reverse proxy, with HTTPS certificates generated by mkcert when available.
The name is inspired by a 1950s telephone switchboard operator: Betty does not become your reverse proxy. She links local domains to running containers and keeps proxy infrastructure out of individual projects.
Betty currently orchestrates:
- Docker
- Docker Compose
- one global Traefik proxy
- mkcert for local HTTPS certificates
Status
Betty is an early prototype. The current goal is a small, Valet-like workflow:
betty dev
betty serve
betty link
betty relink
betty status
betty doctor
betty setup
betty setup --fix
betty unlink
betty stop
betty restThe core workflow does not require a project configuration file. Start your
containers with Docker or Docker Compose, then let Betty link them to local
domains. Projects that want a single command can add missbetty.yml and run
betty dev.
Requirements
- Node.js 24 or newer
- Docker with Docker Compose
- Access to the Docker socket
- mkcert for HTTPS support
On Linux, macOS, WSL, and devcontainers, Betty expects Docker commands such as
docker ps and docker compose version to work from the shell where Betty runs.
Installation
Install without Node.js or npm
Betty publishes standalone binaries on GitHub Releases. The installer scripts verify SHA256 checksums before installation. Release assets are also signed with Sigstore Cosign (keyless certificates).
Current prebuilt targets:
- Linux x64
- Linux arm64
- macOS x64
- macOS arm64
- Windows x64
- Windows arm64
Linux/macOS:
curl -fsSL https://raw.githubusercontent.com/mcKanses/missbetty/main/install.sh | sudo shWindows PowerShell:
irm https://raw.githubusercontent.com/mcKanses/missbetty/main/install.ps1 | iexOptional version pinning:
BETTY_VERSION=v1.3.1 curl -fsSL https://raw.githubusercontent.com/mcKanses/missbetty/main/install.sh | sh$env:BETTY_VERSION = 'v1.3.1'; irm https://raw.githubusercontent.com/mcKanses/missbetty/main/install.ps1 | iexWindows installer options:
- Skip dependency installation (Docker/mkcert):
$env:BETTY_SKIP_DEPS = 'true'; irm https://raw.githubusercontent.com/mcKanses/missbetty/main/install.ps1 | iex- Increase Docker daemon wait timeout (seconds, default 240, minimum 30):
$env:BETTY_DOCKER_WAIT_SECONDS = '420'; irm https://raw.githubusercontent.com/mcKanses/missbetty/main/install.ps1 | iexThe binary install path is:
- Linux/macOS:
/usr/local/bin/betty(or$BETTY_INSTALL_DIR/betty) - Windows:
%LOCALAPPDATA%\\Programs\\betty\\betty.exe(or$env:BETTY_INSTALL_DIR\\betty.exe)
You still need runtime tools for Betty workflows (Docker and optionally mkcert), but Node.js and npm are no longer required for using Betty.
Uninstall standalone binary
Linux/macOS:
curl -fsSL https://raw.githubusercontent.com/mcKanses/missbetty/main/uninstall.sh | shWindows PowerShell:
irm https://raw.githubusercontent.com/mcKanses/missbetty/main/uninstall.ps1 | iexInstall from source (Node.js + npm)
From this repository:
npm install
npm run build
npm linkAfter npm link, the CLI is available as:
betty --helpQuick Start
For project-level orchestration, add missbetty.yml to the project root:
project: my-app
up:
command: docker compose up --build -d
down:
command: docker compose down
domains:
- host: my-app.dev
target: http://127.0.0.1:3000
https:
enabled: true
certificateAuthority: missbetty
permissions:
hosts: prompt
trustStore: prompt
docker: allowedThen start the project:
betty devBetty prepares the local route and prints the available URLs:
https://my-app.devFor the lower-level workflow, start the global proxy once, start your containers yourself, then link a running container:
betty serve
betty link my-app --domain my-app.localhost --port 3000Commands
betty dev
Starts a project from missbetty.yml.
betty dev
betty dev --config ./missbetty.yml
betty dev --dry-runExample:
project: mckanses-auth
up:
command: docker compose --env-file .env -f compose.yml -f compose.override.yml up --build -d
down:
command: docker compose -f compose.yml -f compose.override.yml down
domains:
- host: ory-ui.mckansescloud.dev
target: http://127.0.0.1:5173
- host: api.mckansescloud.dev
target: http://127.0.0.1:8080
https:
enabled: true
certificateAuthority: missbetty
permissions:
hosts: prompt
trustStore: prompt
docker: allowedbetty dev reads the project config, prepares hosts entries and mkcert
certificates, starts Betty's global proxy, writes project routes, runs the
configured up.command, then prints the available URLs. Loopback targets such
as 127.0.0.1 and localhost are routed through host.docker.internal inside
the Traefik container.
Config fields:
| Field | Description |
| --- | --- |
| project | Stable project name used for Betty route file names |
| up.command | Shell command run after hosts, certificates, and proxy routes are ready |
| down.command | Reserved project shutdown command for tools and future workflow support |
| domains[].host | Local domain Betty should expose |
| domains[].target | Local HTTP(S) target for the domain, for example http://127.0.0.1:5173 |
| https.enabled | Enables HTTPS routes and mkcert certificates |
| https.certificateAuthority | Currently supports missbetty |
| permissions.hosts | prompt, allowed, manual, or denied for hosts-file changes |
| permissions.trustStore | prompt, allowed, manual, or denied for mkcert CA setup |
| permissions.docker | prompt, allowed, manual, or denied for Docker commands |
betty serve
Starts Betty's global local switchboard service.
It creates:
~/.betty~/.betty/docker-compose.yml~/.betty/dynamic~/.betty/certs- Docker network
betty_proxy - Traefik container
betty-traefik
Traefik publishes HTTP on host port 80 and HTTPS on host port 443.
Both ports must be available on the host while Betty is running.
Betty keeps routing config globally in ~/.betty/dynamic, and local
certificates in ~/.betty/certs, so individual projects do not need
Betty-specific files.
betty stop
Stops Betty's global local switchboard service.
betty rest is available as a legacy alias with the same behavior.
It runs Docker Compose down against Betty's global compose file:
~/.betty/docker-compose.ymlbetty status
Shows Betty's proxy status and linked domains.
betty status
betty status --short
betty status --long
betty status --json| Option | Description |
| --- | --- |
| --long | Show detailed proxy container info |
| --short | Show a compact linked-domain table |
| --json | Output status as JSON |
| --format <format> | Output format, for example json |
betty link [container]
Links a running container to a local domain.
betty link my-container --domain my-app.localhost --port 3000If required values are missing, Betty asks interactively for:
- container
- domain
- internal container port
Betty links the container into the global Docker network betty_proxy, writes
a route file to ~/.betty/dynamic, and reloads the global Traefik container.
If mkcert is installed, Betty also creates a certificate in ~/.betty/certs
and enables HTTPS for the linked domain.
Use .localhost domains when possible, for example my-app.localhost. Betty
also accepts custom domains such as .dev and can add an append-only hosts
entry for them. Browsers require HTTPS for some TLDs, including .dev, so
mkcert should be installed before linking those domains.
| Option | Description |
| --- | --- |
| --domain <domain> | Target domain, for example my-app.localhost |
| --port <port> | Internal container port |
| --dry-run | Preview planned changes without applying them |
| --open | Open the linked domain in the browser after linking |
betty relink [target]
Updates an existing local domain link.
Use it when the container, domain, or internal port changes:
betty relink
betty relink my-app --port 5173
betty relink --domain my-app.example.dev
betty relink my-app.localhost --container new-container --port 3000If values are missing, Betty asks interactively. When the domain changes to a
custom domain outside .localhost, Betty attempts to add a new append-only
hosts entry. It does not remove the previous hosts entry.
| Option | Description |
| --- | --- |
| --container <container> | New target container |
| --domain <domain> | New linked domain |
| --port <port> | New internal container port |
betty unlink [target]
Removes an existing local domain link.
betty unlink my-app.localhost
betty unlink --domain my-app.localhost
betty unlink --allFor custom domains that are not under .localhost, betty unlink never
removes or rewrites hosts entries.
| Option | Description |
| --- | --- |
| --domain <domain> | Domain to unlink |
| --all | Remove all links at once |
betty config [action] [key] [value]
Reads or updates Betty configuration.
betty config
betty config get <key>
betty config set <key> <value>Development
npm install
npm run build
npm run lint
npm test
npm run test:serial
npm run test:coverageThe default test script runs Jest serially so it works in restricted environments that cannot spawn parallel Jest workers. Coverage also runs serially for the same reason.
Release
Development happens on development. Releases are published from main by
GitHub Actions with semantic-release.
Use Conventional Commits so semantic-release can determine the next version:
fix: Repair npm package contents
feat: Add a new link option
feat!: Change the link command contractRelease behavior:
fix:creates a patch releasefeat:creates a minor releaseBREAKING CHANGE:or!creates a major releaseci:andchore:do not trigger a release by default
Before merging to main, run the full local release check:
npm run release:checkAfter a merge to main, GitHub Actions runs semantic-release. If releasable
commits exist, it creates the Git tag, GitHub release, and npm publish
automatically without pushing a release commit back to main.
Configure the repository secret NPM_TOKEN with an npm automation or granular
access token that can publish missbetty.
The publish workflow uses Node.js 24. Betty itself requires Node.js 24 or newer at runtime.
Manual fallback release scripts are still available:
npm run release:patch
npm run release:minor
npm run release:majorThe fallback scripts are guarded and only run on the main branch.
For emergency manual publishing:
npm run release:publishprepack builds the TypeScript output before npm creates the package. Keep the
manual scripts as fallback only; the normal release path is semantic-release on
main.
Devcontainer
This repository includes a devcontainer setup using Docker-outside-of-Docker. That means Node and TypeScript run inside the devcontainer, while Docker containers are created through the host Docker daemon.
This is suitable for CLI development and Docker orchestration tests. Host browser access to custom domains may still require host-specific handling for the hosts file.
Notes
Betty is not intended to replace Traefik, Caddy, nginx, or Docker Compose. Instead, Betty provides a small CLI layer around them for local development.
License
Betty is released under the MIT License. See LICENSE for the full terms.
Support Betty
If Betty saves you time, consider supporting development:
- GitHub Sponsors: https://github.com/sponsors/mcKanses
- Buy me a coffee: https://buymeacoffee.com/mckanses
Support helps me work on:
- Windows/WSL support
- TLS automation
- better Docker integration
Future work may include:
- better host file handling across Windows, WSL, Linux, and macOS
- cleaner persistent link state
- optional project discovery
Troubleshooting
Port 443 is already in use
If betty serve reports a conflict on 443, list the containers using it:
docker ps --filter "publish=443" --format "table {{.Names}}\t{{.Ports}}"If an old Traefik stack is still running, stop it:
docker stop traefik-traefik-1Then run:
betty serve