Self-host Ploy: your own open-source Vercel alternative

How to self-host Ploy, the open-source Vercel alternative: one Docker command for a test instance, a split Docker Compose setup for production, GitHub App setup, and what you get for free, including databases, queues, workflows, cron, auth, and an AI gateway.

Abstract indigo glow on a dark background representing a self-hosted Ploy instance

Managed deployment platforms are convenient right up to the moment you need your code, environment variables, and databases to stay on infrastructure you control, or the bill stops matching the traffic. At that point the choice is usually "build an internal platform" or "accept it".

Ploy is a third option: an open-source (AGPLv3), self-hostable serverless cloud. Run it on your own servers and you get push-to-deploy from GitHub, a preview URL per branch, serverless workers, SQLite databases, queues, durable workflows, cron, sandboxes, managed auth, and an AI gateway, for free, with no usage limits. This post is a practical guide to self-hosting Ploy as your own Vercel alternative.

What you need

  • Docker and Docker Compose
  • A GitHub OAuth App (sign-in) and a GitHub App (repository access and webhooks)
  • A domain, recommended for anything beyond a test
  • 2 GB of RAM to try it, 4 GB or more for production, and 10 GB or more of disk for deployments

Fastest path: one container

The unified image bundles every service for testing and small installs:

docker run -d \
--name ploy \
--restart unless-stopped \
-p 3002:3002 -p 3005:3005 -p 4001:4001 -p 4002:4002 \
-v ~/ploy_data:/var/lib/postgresql/data \
-v ~/ploy_fs:/app/fs \
-e AUTH_SECRET=your-secret-key-here \
-e GITHUB_CLIENT_ID=your-github-client-id \
-e GITHUB_CLIENT_SECRET=your-github-client-secret \
-e GITHUB_WEBHOOK_SECRET=your-webhook-secret \
ghcr.io/meetploy/ploy-unified:latest

The dashboard is on :3002, the API on :4002, docs on :3005, and the handler that serves deployed apps on :4001. Pin latest to a release tag before relying on it.

There is also an install script that detects your Linux distribution, installs Docker if needed, writes a sample .env, and starts the services:

curl -fsSL https://raw.githubusercontent.com/meetploy/ploy/main/scripts/install.sh | sudo bash -s -- simple

Use prod instead of simple for the split layout below.

Production path: split services

For production, run each service in its own container:

git clone https://github.com/meetploy/ploy.git
cd ploy
cp .env.example .env
docker compose -f infra/docker-compose.split.yml up -d

The split layout runs Postgres, Redis, the API, the dashboard, the builder, the handler, and the docs as separate containers. Only the API, dashboard, and handler should accept traffic from outside the service network; the internal API, database service, broker, and sandbox control plane stay private, which the bundled Compose files do by not publishing their ports. On Kubernetes or another orchestrator, enforce the same boundary with network policies.

GitHub setup

Two GitHub registrations, both created from your GitHub settings:

  1. OAuth App for signing in to the dashboard. Callback URL: https://ploy.yourdomain.com/api/auth/callback/github.
  2. GitHub App for reading repositories and receiving pushes. Webhook URL: https://api.yourdomain.com/webhooks/github, with Contents and Metadata read permissions, Webhooks read and write, and the Push and Repository events.

Put the client id and secret, app id, private key, and webhook secret in .env:

AUTH_SECRET=... # openssl rand -hex 32
BETTER_AUTH_URL=https://ploy.yourdomain.com
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...
GITHUB_APP_ID=...
GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
GITHUB_WEBHOOK_SECRET=... # openssl rand -hex 32
NEXT_PUBLIC_API_URL=https://api.yourdomain.com

If you do not want GitHub in the loop at all, projects can deploy from Ploy Git, a repository hosted by your Ploy instance that accepts plain git push.

TLS and domains

Put a reverse proxy in front: the dashboard on ploy.yourdomain.com, the API on api.yourdomain.com, and a wildcard such as *.ploy-deployments.yourdomain.com pointed at the handler so every deployment and preview gets its own hostname. A Caddy configuration is in the self-host guide. Keep the proxy's idle upstream keep-alive under 5 seconds (keepalive 3s in Caddy), and set PUBLIC_PROTOCOL=https so workers see the right scheme in request.url.

Storage choices

  • Project databases default to DB_STORAGE_MODE=basic, one SQLite file per project under DB_STORAGE_DIR. Set DB_STORAGE_MODE=turso-cloud to place them in Turso Cloud for managed replication and point-in-time restore.
  • Sandboxes default to SANDBOX_PROVIDER=docker, running on the sandbox host with per-project network isolation. SANDBOX_PROVIDER=boat uses the hosted Boat provider instead, with no Docker daemon needed.
  • Worker CPU limits are configurable per plan with WORKER_CPU_LIMIT_MS_* variables; organizations on a self-hosted instance use the Free limits unless you raise them.

What self-hosting gets you

On hosted Ploy Self-hosted
Free plan: 3 repositories, 1 concurrent No usage limits
Pro: $10/month $0, your infrastructure
Data on Ploy's infrastructure Code, env vars, and databases stay with you
Ploy operates it You operate it: updates, backups, monitoring

The developer experience is identical: the same ploy.yaml, the same @meetploy/* packages, the same ploy dev emulator, and the same dashboard.

Keeping it running

docker compose -f infra/docker-compose.split.yml logs -f # logs
docker exec ploy-postgres pg_dump -U postgres ploy > backup.sql # backup
git pull origin main && docker compose pull && docker compose up -d # update

Set LOG_LEVEL=debug in .env when something misbehaves, and check the builder and handler logs first for failed deployments.

Frequently Asked Questions

Is self-hosted Ploy feature-complete?

Yes. Databases, queues, workflows, cron, sandboxes, auth, and the AI gateway are part of the open-source platform. Only the ee/ directory is under a commercial license.

Can I run it on a single small server?

Yes. The unified container runs on 2 GB of RAM for testing. Use the split Compose file and more memory for production workloads.

Does it support Next.js, Vite, and TanStack Start?

Yes, the same frameworks as hosted Ploy, with the same ploy.yaml configuration. See Deploy Next.js on Ploy.

What license is Ploy under?

AGPLv3, with the ee/ directory under a separate commercial license. See the LICENSE file for the exact terms.

Get started