Skip to content

DocsRun it yourself

Self-host OpenPromo with Docker

How to self-host OpenPromo with Docker: one command on your computer or a cloud server, HTTPS for your own domain, media storage, updates and backups.

OpenPromo is open source under the MIT license, and everything the hosted app runs is in the repository: the dashboard, the API with its MCP server, background worker and renderer, the public site and the database schema. The Docker image includes headless Chrome, FFmpeg and fonts for rendering. With Docker, one command starts all of it.

Ways to run OpenPromo
WhereGood forYou need
Your own computerTrying it out, or using it just for yourselfDocker Desktop (Windows, macOS) or Docker (Linux), with about 4 GB of memory for it
A cloud serverUsing it from anywhere, with a team, on your own domainA server with about 2 vCPU and 4 GB of memory, and a domain
From sourceChanging the codeNode.js 26, pnpm 10 and Postgres

On your own computer

  1. Install Docker

    Install Docker Desktop on Windows or macOS, or Docker Engine on Linux, and start it. Give it at least 4 GB of memory: the first build compiles the dashboard and the site, and rendering video runs Chrome and FFmpeg.

  2. Get the code

    Terminal
    git clone https://github.com/duckduckdeploy/openpromo.git openpromo
    cd openpromo
  3. Set two secrets

    Terminal
    cp .env.example .env
    
    # Then open .env and set these two:
    AUTH_SECRET=        # any 32+ random characters
    ENCRYPTION_KEY=     # the output of: openssl rand -base64 32

    AUTH_SECRET signs sign-in sessions. ENCRYPTION_KEY encrypts the YepAPI keys you store; keep a copy somewhere safe, because they cannot be read without it.

    Terminal
    # Generate both with Docker if you have no openssl:
    docker run --rm alpine sh -c "apk add -q openssl && echo AUTH_SECRET=\$(openssl rand -hex 32) && echo ENCRYPTION_KEY=\$(openssl rand -base64 32)"
  4. Start it

    Terminal
    docker compose up -d --build

    The first build takes a few minutes. Postgres starts, the database is set up, and then the API, dashboard and site come up. Open http://localhost:3200, create your account and follow the setup.

Addresses on your computer
AddressWhat it is
http://localhost:3200The dashboard: where you work
http://localhost:3101This site and the docs, served locally
http://localhost:4100The API, and the MCP server at /mcp for your AI agent

These addresses are only reachable from your computer. To stop OpenPromo, run docker compose down; your database and media files stay in Docker volumes until you remove them with docker compose down -v.

On a cloud server, with your own domain

Any provider that gives you a Linux server works: a virtual server with about 2 vCPU, 4 GB of memory and 40 GB of disk is enough for one person or a small team. Videos and pictures add up, so give it more disk if you render a lot. Use Ubuntu 24.04 or Debian 12.

  1. Point your domain at the server

    Create DNS records for your domain, app. and api. on it (for example example.com, app.example.com and api.example.com), all pointing at the server's IP address. Open ports 22, 80 and 443 in the provider's firewall, and nothing else.

  2. Install Docker and get the code

    On the server
    # Ubuntu or Debian: install Docker
    curl -fsSL https://get.docker.com | sh
    
    # Get OpenPromo
    git clone https://github.com/duckduckdeploy/openpromo.git openpromo
    cd openpromo
    cp .env.example .env
  3. Set the secrets and your domain

    .env
    # .env on a server with the domain example.com
    AUTH_SECRET=...
    ENCRYPTION_KEY=...
    POSTGRES_PASSWORD=...           # a long random password
    
    OPENPROMO_DOMAIN=example.com
    APP_URL=https://app.example.com
    WEB_URL=https://example.com
    PUBLIC_API_URL=https://api.example.com

    The public addresses are built into the dashboard and the site, so set them before you start, and rebuild if you change them.

  4. Start it with HTTPS

    On the server
    docker compose --profile https up -d --build

    The https profile adds Caddy in front, which gets certificates from Let's Encrypt and renews them on its own. The app, site and API listen only on the server itself; Caddy is the only thing facing the internet.

Updating

Terminal
cd openpromo
git pull
docker compose up -d --build        # add --profile https on a server

Database changes are applied automatically when the API starts. Back up first if your data matters to you.

OpenPromo keeps two Postgres databases: openpromo for accounts, keys and usage, and openpromo_data for projects, brand profiles, ideas, creations and renders. The second is created on first start, on the same server. To keep it elsewhere, set DATA_DATABASE_URL. Files live in the media Docker volume, mounted at MEDIA_DIR (/data/media) inside the API.

Backups

Terminal
# Back up both databases to files: accounts and keys, then project data
docker compose exec -T postgres pg_dump -U openpromo openpromo | gzip > openpromo-$(date +%F).sql.gz
docker compose exec -T postgres pg_dump -U openpromo openpromo_data | gzip > openpromo_data-$(date +%F).sql.gz

# Restore them into an empty install
gunzip -c openpromo-2026-10-15.sql.gz | docker compose exec -T postgres psql -U openpromo openpromo
gunzip -c openpromo_data-2026-10-15.sql.gz | docker compose exec -T postgres psql -U openpromo openpromo_data

# Back up the media files (screenshots, uploads, AI pieces and renders)
docker compose cp api:/data/media ./media-$(date +%F)

Keep .env with your backups: the stored YepAPI keys cannot be read without the same ENCRYPTION_KEY. On a server, run the backup commands nightly with cron and copy the files off the machine.

Settings

Optional settings in .env
SettingWhat it does
RENDER_CONCURRENCYRenders at once, 1 by default. Each one runs Chrome and FFmpeg; raise it only on a bigger server.
MEDIA_JOB_CONCURRENCYAI media jobs (voice, music, pictures, clips) at once, 4 by default.
INGEST_MAX_PAGESPages read when building a brand profile, 8 by default.
API_BIND, APP_BIND, WEB_BINDWhich address each service listens on. 127.0.0.1 by default, so only this machine can reach them.
POSTGRES_PASSWORDThe database password. Set a long random one on a server.

After changing .env, run docker compose up -d again.

From source, for development

Terminal
pnpm install
docker compose up -d postgres       # or point DATABASE_URL at your own Postgres
pnpm db:migrate
pnpm dev

pnpm dev runs the API on :4100, the dashboard on :3200 and the site on :3101 with live reload. pnpm typecheck and pnpm test run the checks; the tests need no database or network, because they use an in-memory Postgres and a stand-in for YepAPI.

Connect your agent next

Connect your AI agent to your own install: your MCP endpoint is {API_URL}/mcp. See Connect an agent over MCP.