Doppel is a CLI-driven HTTP reverse proxy. The name is from "doppelganger": it stands in front of, or in place of, a real backend, so clients of that backend can be developed and tested against a realistic, deliberately degraded, or entirely absent upstream. It forwards traffic to a configured upstream, injects controlled latency and loss, and replaces selected endpoints with templated mock responses.
curl -fsSL https://raw.githubusercontent.com/lorem-dev/doppel/main/scripts/install.sh | shPrebuilt binaries are published for macOS on Apple Silicon and Linux on x86-64
and arm64. Anywhere else, cargo install --path crates/doppel-cli. See
the documentation.
Or run the image:
docker run --rm -p 8080:8080 -p 8081:8081 \
-v "$PWD/config:/etc/doppel" \
loremdev/doppel:1.2.3-alpinePublished for linux/amd64 and linux/arm64, with no latest tag -- pin the
version. See
Running in Docker.
- Forwards HTTP requests to a configured upstream, streaming both request and response bodies rather than buffering them.
- Resolves which of several configured proxies handles a request, either by a header value or by falling back to a configured default.
- Injects configured latency and loss (dropped requests answered with a configured status), independently per proxy.
- Loads, validates and reloads its YAML configuration from disk, and reloads
a running server's configuration over a local control socket
(
doppel config reload) without a restart. - Logs one line per request to stdout, in JSON or human-readable text.
- Serves an admin HTTP API on a second port: proxy CRUD with optimistic concurrency, template upload, reload, a status endpoint, Prometheus metrics and a generated Swagger UI, behind token access control. See the documentation.
- Serves a browser dashboard from that same port's root, compiled into the
binary: the proxy list, a form over the whole proxy document, mock templates,
status and reload. It is a client of the admin API and bound by the same token
rules, so it offers only what the caller may actually do -- and works without a
token wherever the API does.
admin.dashboard: falseturns it off. See the documentation. - Reports errors to Sentry, optionally, behind the
sentrycargo feature. - Stores its configuration in a YAML file or in PostgreSQL, selected by
--store, withconfig pushandconfig pullto move a configuration between the two. See the documentation.
- No TCP proxying. A
type: tcpproxy is rejected at load with a message saying so, rather than being quietly ignored.
Mocked responses and Jinja2 templating are implemented -- see the documentation.
The full documentation is published at
https://lorem-dev.github.io/doppel/. Its source is docs/, and it builds
with mkdocs and the Material theme; Python tooling here is driven by uv,
never pip:
uv run --with-requirements docs/requirements.txt mkdocs serveThe dashboard's source is frontend/; building it needs Node, and the binary
embeds whatever frontend/dist holds at compile time:
npm --prefix frontend ci && npm --prefix frontend run build
cargo build --release -p doppel-cliSkipping that leaves a working binary whose root answers 503 and says so.
The workspace layout -- the crates, what each owns, and the dependency direction -- is in the architecture page.
.superpowers/specs/ holds the design documents; that directory is git-ignored
and not shipped.
Prerequisites: a stable Rust toolchain via rustup, edition 2024 (see
Cargo.toml for the pinned rust-version).
make release # the dashboard, then the binary
mkdir -p config && cp main.example.yaml config/main.yaml # config/ is git-ignored
./target/release/doppel serve --config config/main.yamlmake help lists every target: make gate runs the whole check suite,
make image-rebuild builds the container image from scratch, make docs-serve
serves the documentation. cargo build --release works too, and skips the
dashboard -- which then answers 503 at the admin root and says so.
cargo run -- <args> works the same way during development.
doppel serve-- run the proxy. Bindsserver.host:server.portfor traffic and a Unix domain socket atcontrol.socketfor the reload channel. Runs until it receives a shutdown signal, draining in-flight requests first.doppel config validate-- parse and validate the configuration, printing every violation found (one per line, with its config path) and exiting non-zero if there is one.doppel config reload-- ask a running server (overcontrol.socket) to reload its configuration from disk, and report the resulting revision and proxy count, or the violations that made it refuse the new configuration.
Configuration path, store selection and similar are set with flags or their
DOPPEL_* environment equivalents; run any command with --help for the
full list.
DOPPEL_EXTERNAL_URL names the address clients reach Doppel at, for rewriting an
upstream's redirects. Needed behind a port mapping or an ingress; otherwise
server.host:server.port is used.
DOPPEL_ADMIN_TOKENS and DOPPEL_SENTRY_DSN keep the two credentials out of the
configuration file. Both override what the document says.
server:
host: "0.0.0.0"
port: 8080
admin:
host: "0.0.0.0"
port: 8081
tokens: []
access: {}
upload:
limit: 1Mi
proxies:
- name: backend
type: http
url: "https://example.com/"
resolve:
type: defaultSee main.example.yaml at the repository root for a fuller reference covering
tokens, access control, and fault injection. Note that every admin action --
reads included -- defaults to the admin group: a proxy document carries the
headers that proxy injects upstream, so a public listing would publish them.