How to Self-Host OpenSEO Using Docker: A Complete Deployment Guide
You can self-host OpenSEO using Docker by pulling the pre-built image from GitHub Container Registry, configuring a local .env file with your DataForSEO API key, and running docker compose up -d to launch the container on port 3001.
OpenSEO from the every-app/open-seo repository is a self-hostable SEO analysis platform that bundles a Node.js runtime with Cloudflare's workerd engine. The Docker deployment strategy ensures all dependencies—from the Node 22 runtime to build tools—are packaged together, eliminating environment-specific conflicts. According to the source code, the container uses the full Node 22 image specifically to provide a proper CA trust store for the internal workerd process when making outbound HTTPS calls.
Architecture Overview
The Docker implementation consists of four key components that work together to provide a stateless, reproducible deployment.
Dockerfile.selfhost
The Dockerfile.selfhost defines the production image based on the full Node 22 container. It installs pnpm, copies the application source, runs pnpm install --frozen-lockfile for deterministic dependency resolution, and configures the entrypoint to execute docker-entrypoint.sh. The Dockerfile also defines a native Docker health-check that pings the /api/health endpoint every 30 seconds after an initial 5-minute start period, ensuring the container is only marked healthy once the Vite SSR build and migrations complete.
docker-entrypoint.sh
Located at the repository root, docker-entrypoint.sh orchestrates the startup sequence. It executes pre-flight database migrations, conditionally runs the Vite SSR build (caching artifacts in the open_seo_data volume), and finally launches the HTTP server on the configurable PORT environment variable. This script is critical for handling the first-run build process while ensuring subsequent restarts remain fast.
compose.yaml
The compose.yaml file declares the open-seo service, mapping the container port to your host and injecting all environment variables from a local .env file. It sets essential runtime flags including AUTH_MODE=local_noauth and CLOUDFLARE_INCLUDE_PROCESS_ENV=true, and mounts the named volume open_seo_data to persist build caches between container restarts.
.env.example
This template file documents all required and optional configuration values. The mandatory DATAFORSEO_API_KEY expects a base64-encoded string of your email:password credentials from DataForSEO. Optional variables include AI service keys and telemetry toggles.
Prerequisites
Before deploying, ensure you have:
- Docker Engine 20.10+ and Docker Compose v2+
- A DataForSEO account and API credentials (for the
DATAFORSEO_API_KEY) - At least 2GB of available disk space for the Node 22 base image and build artifacts
Step-by-Step Deployment Guide
1. Prepare Environment Variables
Clone the repository and copy the example environment file:
cp .env.example .env
Edit the .env file to set your DataForSEO credentials. Encode your email and password in base64:
echo -n "your-email@example.com:your-password" | base64
Paste the resulting string as the value for DATAFORSEO_API_KEY.
2. Launch the Container
Run the pre-built image from GitHub Container Registry:
docker compose up -d
This command pulls ghcr.io/every-app/open-seo:latest (or a custom tag specified in OPEN_SEO_IMAGE), mounts your .env file, and starts the service in detached mode.
3. Monitor Startup and Access
Watch the build process and server initialization:
docker compose logs -f open-seo
The first startup performs a Vite SSR build, which may take several minutes. Once complete, access the application at:
http://localhost:3001
Replace 3001 with your custom PORT value if configured differently in .env.
Building Custom Images
When modifying the source code locally, build a custom image instead of using the registry version:
docker build -f Dockerfile.selfhost -t open-seo:local .
OPEN_SEO_IMAGE=open-seo:local docker compose up -d
This workflow uses the Dockerfile.selfhost definition while allowing you to test changes before pushing.
Configuration and Telemetry
OpenSEO includes opt-out telemetry that sends anonymized usage data. Disable it by adding either variable to your .env file:
echo "OPENSEO_TELEMETRY_DISABLED=1" >> .env
# Or:
echo "DO_NOT_TRACK=1" >> .env
After modifying environment variables, recreate the container to apply changes:
docker compose up -d --force-recreate open-seo
Health Monitoring and Troubleshooting
The container exposes a health-check endpoint at /api/health as defined in docker-entrypoint.sh and configured in Dockerfile.selfhost. Docker monitors this endpoint with a 5-minute grace period to accommodate the initial Vite build.
Check container health status:
docker ps
If the status remains (health: starting) beyond 5 minutes, inspect logs for build errors or missing environment variables like DATAFORSEO_API_KEY.
Summary
- Base Image: The
Dockerfile.selfhostuses the full Node 22 image to provide CA certificates required by the Cloudflare workerd process. - Startup Process: The
docker-entrypoint.shhandles database migrations, conditional Vite SSR builds, and server initialization. - Configuration: Copy
.env.exampleto.envand set the base64-encodedDATAFORSEO_API_KEYbefore starting. - Persistence: Build artifacts are cached in the
open_seo_dataDocker volume for faster subsequent restarts. - Health Checks: The container reports healthy status only after passing the
/api/healthcheck, with a 5-minute startup window for initial builds.
Frequently Asked Questions
What is the default port for OpenSEO when running in Docker?
The default port is 3001, configurable via the PORT environment variable in your .env file. The compose.yaml maps this container port to the same port on your host machine unless modified.
How do I disable telemetry in my self-hosted instance?
Set either OPENSEO_TELEMETRY_DISABLED=1 or DO_NOT_TRACK=1 in your .env file, then recreate the container with docker compose up -d --force-recreate. This prevents the application from sending anonymized usage data according to the configuration documented in docs/SELF_HOSTING_DOCKER.md.
Can I build OpenSEO from source instead of using the pre-built image?
Yes. Use docker build -f Dockerfile.selfhost -t open-seo:local . to build locally, then specify your custom image tag by setting OPEN_SEO_IMAGE=open-seo:local before running docker compose up -d.
Why does the Dockerfile use the full Node 22 image instead of Alpine?
The full Node 22 image provides a complete CA trust store required by the internal Cloudflare workerd process for making outbound HTTPS calls. According to the source code in Dockerfile.selfhost, Alpine or slim variants lack the necessary certificate infrastructure for this specific architecture.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →