# How to Self-Host OpenSEO Using Docker: A Complete Deployment Guide

> Learn to self-host OpenSEO with Docker. Deploy the complete guide by pulling the image, configuring your API key, and running a simple Docker command for immediate setup on port 3001.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: how-to-guide
- Published: 2026-08-05

---

**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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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:

```bash
cp .env.example .env

```

Edit the `.env` file to set your DataForSEO credentials. Encode your email and password in base64:

```bash
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:

```bash
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:

```bash
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:

```bash
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:

```bash
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:

```bash
echo "OPENSEO_TELEMETRY_DISABLED=1" >> .env

# Or:

echo "DO_NOT_TRACK=1" >> .env

```

After modifying environment variables, recreate the container to apply changes:

```bash
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`](https://github.com/every-app/open-seo/blob/main/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:

```bash
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.selfhost` uses the full Node 22 image to provide CA certificates required by the Cloudflare workerd process.
- **Startup Process**: The [`docker-entrypoint.sh`](https://github.com/every-app/open-seo/blob/main/docker-entrypoint.sh) handles database migrations, conditional Vite SSR builds, and server initialization.
- **Configuration**: Copy `.env.example` to `.env` and set the base64-encoded `DATAFORSEO_API_KEY` before starting.
- **Persistence**: Build artifacts are cached in the `open_seo_data` Docker volume for faster subsequent restarts.
- **Health Checks**: The container reports healthy status only after passing the `/api/health` check, 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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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.