Self Host
Browser (Monitors)

Browser (Monitors)

The DuckDB deployment plus Node, @playwright/test, and headless Chromium baked in, so browser monitors run inside the container with zero extra moving parts (SYNTHETICS_BROWSER_MODE=embedded is set by default). HTTP and TCP checks work in every image; this image exists only for the browser ones.

Pick this when you want Playwright uptime checks with one container. If you already run another image (SQLite, DuckDB, minimal, or all-in-one), you do not need to switch: set SYNTHETICS_BROWSER_MODE=remote there and execute browser checks with the traceway-runner binary instead. Running Traceway as a plain binary with no Docker at all? See Without Docker below.

Operational notes on top of the DuckDB image:

  • The image is significantly larger (Chromium and its system libraries add several hundred MB).
  • Chromium needs shared memory. Docker's default 64MB /dev/shm crashes tabs on heavier pages; run with --shm-size=1g (the compose file sets it). The backend also passes --disable-dev-shm-usage as a fallback.
  • Leave memory headroom. Each concurrent browser check runs a Chromium instance (roughly 300 to 500MB); SYNTHETICS_BROWSER_CONCURRENCY defaults to 2. Budget DUCKDB_MEMORY_LIMIT accordingly.
  • Browser checks execute user-authored scripts inside this container, as root with Chromium's sandbox off (the standard containerized-Playwright trade-off). Anyone with write access to a project can author them; if that trust model does not fit, use remote mode instead.
  • The bubblewrap sandbox usually cannot start inside Docker. The image ships bubblewrap and SYNTHETICS_BROWSER_SANDBOX=auto, but creating a user namespace inside a container is the host's decision, not the image's. Measured on Docker with an Ubuntu 24.04 host kernel, it is refused under the default security options, under --security-opt seccomp=unconfined, and under --cap-add SYS_ADMIN. It works only with --privileged and sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 on the host. Expect sandbox: off in the startup log on a normal Docker host, which is the same isolation this image has always had: the container boundary and the env allowlist. Check the log line if you care which one you got. When check authors are not fully trusted, remote mode on a dedicated host is the configuration that actually sandboxes.

Quick Start

docker pull ghcr.io/tracewayapp/traceway:browser
 
docker run -d --name traceway \
  -p 80:80 \
  --shm-size=1g \
  -v "$(pwd)/traceway-data:/data" \
  -e JWT_SECRET="your-jwt-secret-min-32-characters-long" \
  -e APP_BASE_URL="https://traceway.example.com" \
  -e DUCKDB_MEMORY_LIMIT="2GB" \
  ghcr.io/tracewayapp/traceway:browser

Or with compose: docker compose -f docker-compose.browser.yml up -d.

After starting, open http://localhost/register to create your first account, then create a monitor under Monitors.

Build from Source

docker build -f Dockerfile.browser -t traceway:browser .

The pinned @playwright/test version lives in docker/playwright/package.json; the matching Chromium build is downloaded at image build time, never at runtime.

Without Docker

Embedded mode does not actually require this image. The image is packaging; the real requirements are node on PATH and a harness directory containing @playwright/test plus a downloaded Chromium. If you run the Traceway backend as a plain binary (a single EC2 instance, a bare-metal box), you can enable browser monitors on that same host:

# One-time harness setup (Node 18+ required)
sudo mkdir -p /opt/traceway-playwright && cd /opt/traceway-playwright
npm init -y && npm i @playwright/test
npx playwright install --with-deps chromium

Then add to the backend's environment and restart:

SYNTHETICS_BROWSER_MODE=embedded
SYNTHETICS_PLAYWRIGHT_DIR=/opt/traceway-playwright

The backend validates the harness at startup and fails fast with a pointer here if node or the Playwright CLI is missing. --with-deps covers apt-based distros; on rpm-based distros (Amazon Linux) install Chromium's shared libraries with your package manager.

Keep in mind what embedded means: browser checks are user-authored scripts executing on the host that runs your backend, and each concurrent Chromium costs roughly 300 to 500MB (SYNTHETICS_BROWSER_CONCURRENCY, default 2). If either bothers you, run the checks somewhere else with a remote runner instead. Both paths work without Docker.

Configuration

Everything from the DuckDB page applies unchanged. On top of that:

VariableDefaultDescription
SYNTHETICS_BROWSER_MODEembedded (in this image)off, embedded, or remote. Other images default to off.
SYNTHETICS_BROWSER_CONCURRENCY2Concurrent Chromium instances.
SYNTHETICS_HTTP_CONCURRENCY8Concurrent http/tcp probes.
SYNTHETICS_POLL_SECONDS15Scheduler tick for due checks.
SYNTHETICS_ALLOW_PRIVATE_TARGETStrueSet false to refuse checks that resolve to private/LAN addresses (re-checked at connect time).
SYNTHETICS_SCREENSHOT_RETENTION_DAYS30Days to keep failure screenshots under /data/storage/synthetics/. 0 disables the cleanup.
SYNTHETICS_PLAYWRIGHT_DIR/opt/traceway-playwrightThe baked-in Playwright harness; only override for custom builds.

Access Points

Identical to the DuckDB image. /api/health/deep additionally reports a synthetics block (queue depth, checks down, connected runners); it is gated by the HEALTH_DEEP_TOKEN deploy secret.