Standalone Containers
Each service has its own Dockerfile and can be built and run independently. All builds use the repository root as their build context, including web and docs.
For deployment platforms, set the build context to . and the Dockerfile path to apps/web/Dockerfile or apps/docs/Dockerfile. The shared root .dockerignore supports tools that relocate the Dockerfile and omit its adjacent Dockerfile.dockerignore.
Scraper API
The API image is published to GHCR for release tags. Pull it directly - no local build needed.
# Pull
docker pull ghcr.io/germondai/trawl:latest
# Run (no Redis - session caching disabled, scraping still works)
docker run -d \
--name trawl \
-p 8191:8191 \
--shm-size=1gb \
ghcr.io/germondai/trawl:latest
# Run (with external Redis)
docker run -d \
--name trawl \
-p 8191:8191 \
--shm-size=1gb \
-e REDIS_URL=redis://your-redis-host:6379 \
-e BROWSER_POOL_SIZE=1 \
ghcr.io/germondai/trawl:latestOlder CPUs & Synology NAS
If your host CPU doesn't support AVX2 - older Synology NAS units, Atom/Celeron-era hardware, pre-2013 x86_64 - use the :baseline tag instead. Same image, same commands, built on Bun's baseline runtime.
Standard Bun requires Linux kernel 5.1+ (5.6+ recommended); the baseline build degrades gracefully on kernels as old as 3.10 (RHEL 7-era) - covering most Synology DSM installs out of the box.
# Pull
docker pull ghcr.io/germondai/trawl:baseline
# Run
docker run -d \
--name trawl \
-p 8191:8191 \
--shm-size=1gb \
ghcr.io/germondai/trawl:baselineNot sure which tag you need?
Try :latest first. If the container exits immediately or crash-loops, switch to :baseline for compatibility with older CPUs and kernels. See the Docker image tags.
To build from source instead:
# Run from the repo root
docker build -f apps/api/Dockerfile -t trawl .
docker run -d --name trawl -p 8191:8191 --shm-size=1gb trawlTo build the baseline variant from source, use apps/api/Dockerfile.baseline instead:
docker build -f apps/api/Dockerfile.baseline -t trawl:baseline .
docker run -d --name trawl -p 8191:8191 --shm-size=1gb trawl:baselineBoth API Dockerfiles omit the spoofed macOS and Windows font bundles by default. TRAWL detects their absence and limits these compact images to Linux fingerprints, keeping the advertised OS, font probes, and rendered output consistent. To retain the complete OS pool, build with:
docker build \
--build-arg CAMOUFOX_KEEP_SPOOFED_OS_FONTS=1 \
-f apps/api/Dockerfile \
-t trawl .This adds approximately 891 MB to the image and is intentionally available only as a custom build. Published images remain compact and use Linux fingerprints. Other build arguments pin or validate bundled dependencies and normally should not be overridden: UBO_VERSION, UBO_AMO_FILE_ID, UBO_SHA256, GEOLITE_CITY_MIN_BYTES, and (for the baseline image) BUN_VERSION.
Build context
Both API Dockerfiles (apps/api/Dockerfile and apps/api/Dockerfile.baseline) require the repo root as the build context because they copy workspace packages (packages/types, packages/browser, packages/tiers). Always run docker build from the repo root with -f apps/api/Dockerfile (or -f apps/api/Dockerfile.baseline).
Why pull instead of build?
Building the API image downloads the Camoufox Firefox binary (~660 MB) and compiles dependencies - takes 5-10 minutes on first run. The published image on GHCR skips all of that.
Web (landing page)
# Build - run from repo root
docker build -f apps/web/Dockerfile -t trawl-web .
# Run
docker run -d \
--name trawl-web \
-p 3000:80 \
trawl-webStatic HTML served by nginx. No runtime dependencies.
Docs
# Build - run from repo root
docker build -f apps/docs/Dockerfile -t trawl-docs .
# Run
docker run -d \
--name trawl-docs \
-p 3001:80 \
trawl-docsAccess at http://localhost:3001. Static nginx, no runtime dependencies.
For subdomain deployment (e.g. docs.yourdomain.com), point the subdomain at port 3001 via your reverse proxy - no path configuration needed.
Useful commands
Swap :latest for :baseline in any command below if you're on older/no-AVX2 hardware.
# Check what's running
docker ps
# Tail logs
docker logs -f trawl
# Stop and remove
docker stop trawl && docker rm trawl
# Update the scraper to latest
docker pull ghcr.io/germondai/trawl:latest
docker stop trawl && docker rm trawl
docker run -d --name trawl -p 8191:8191 --shm-size=1gb ghcr.io/germondai/trawl:latest