CapRover: Free Heroku Alternative on Your Own VPS
PROGRAMMING LANGUAGES March 14, 2026, 11:30 a.m.

CapRover: Free Heroku Alternative on Your Own VPS

If you’ve ever felt the sting of Heroku’s sudden pricing changes, you’re not alone. Many developers love the simplicity of “push‑to‑deploy,” but the free tier’s limitations can quickly become a bottleneck. Enter CapRover—an open‑source PaaS that runs on any VPS you control, giving you Heroku‑style workflows without the surprise bills. In this guide we’ll walk through why CapRover is a solid free alternative, how to spin it up on a fresh VPS, and how to deploy real‑world apps with just a few commands.

What Is CapRover?

CapRover is a lightweight, Docker‑based platform‑as‑a‑service that abstracts away the complexities of container orchestration. Think of it as a self‑hosted Heroku that uses Docker under the hood, allowing you to deploy any language or framework that can run in a container. The core of CapRover is a single Node.js process that talks to the Docker daemon, exposing a clean web UI and a CLI for one‑click deployments.

Key Features

  • One‑click app creation via the dashboard or caprover CLI.
  • Built‑in SSL with Let’s Encrypt, auto‑renewed for free.
  • Zero‑downtime deployments using Docker’s rolling update strategy.
  • Support for custom domains, environment variables, and persistent volumes.
  • Free, open‑source, and community‑driven with regular updates.

Why Choose CapRover Over Heroku?

Heroku’s appeal lies in its developer‑first experience, but its pricing model can become prohibitive as traffic grows. CapRover, on the other hand, lets you keep the “push‑to‑deploy” magic while you retain full control over resources and costs. You pay only for the VPS you run, which can be as cheap as $5/month on providers like DigitalOcean, Linode, or Hetzner.

Beyond cost, CapRover gives you:

  1. Full root access – you can tweak the underlying OS, install custom binaries, or run background jobs that Heroku’s dynos would block.
  2. Unlimited add‑ons – spin up PostgreSQL, Redis, or MongoDB containers directly from the dashboard, without hunting for third‑party services.
  3. Vendor lock‑in freedom – move your VPS or migrate to a larger cluster without rewriting deployment scripts.

Getting Started: Provision a VPS

The first step is to acquire a virtual private server. For this tutorial we’ll use a $5/month Droplet on DigitalOcean, but any Ubuntu 20.04+ server will work. After creating the droplet, note its public IP address and SSH credentials.

# Replace with your own IP and user
ssh root@YOUR_VPS_IP

Once logged in, update the package index and install a few prerequisites:

apt-get update && apt-get upgrade -y
apt-get install -y curl git build-essential

Secure Your Server

  • Create a non‑root user with adduser deployer and grant sudo rights.
  • Set up a basic firewall using ufw allow OpenSSH && ufw enable.
  • Disable password authentication in /etc/ssh/sshd_config and rely on SSH keys.
Pro tip: Enable automatic security updates with apt-get install unattended-upgrades to keep the server patched without manual intervention.

Installing CapRover

CapRover provides a one‑liner installer that pulls the latest Docker engine and sets up the CapRover service. Run the command as root (or with sudo) on your fresh VPS:

bash <(curl -s https://raw.githubusercontent.com/caprover/caprover/master/setup.sh)

The script will:

  1. Install Docker CE (if not already present).
  2. Create a Docker network called captain-network.
  3. Deploy the CapRover “captain” container on port 3000.

After installation, open a browser and navigate to http://YOUR_VPS_IP:3000. You’ll be greeted by the CapRover setup wizard. Choose a strong admin password, then click “Create New Server.” The wizard will automatically generate a captain.rootDomain (e.g., myapp.captain.root) for internal testing.

Link a Custom Domain

To use a real domain, add a DNS A record pointing to your VPS IP, then in the CapRover dashboard go to Apps → Settings → SSL and enable Let’s Encrypt. CapRover will handle the certificate issuance and renewal for you.

Deploying a Node.js Application

Let’s deploy a simple Express API that returns a random joke. First, create a new Git repository on your local machine:

mkdir node-joke && cd node-joke
git init
npm init -y
npm install express

Create index.js with the following content:

const express = require('express');
const app = express();
const jokes = [
  "Why do programmers prefer dark mode? Because light attracts bugs!",
  "I told my computer I needed a break, and it said 'No problem, I'll go to sleep.'",
  "Why did the developer go broke? Because he used up all his cache."
];
app.get('/joke', (req, res) => {
  const joke = jokes[Math.floor(Math.random() * jokes.length)];
  res.json({ joke });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server listening on ${PORT}`));

Next, add a minimal Dockerfile so CapRover can build the image:

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "index.js"]

Commit your changes:

git add .
git commit -m "Initial commit"

Now open the CapRover dashboard, click “Create New App,” give it a name (e.g., joke-api), and enable “One‑Click Docker Build.” Paste the Git repository URL (GitHub, GitLab, or a private repo with SSH access). CapRover will clone the repo, run docker build, and expose the service on http://joke-api.YOUR_DOMAIN.

Test the endpoint:

curl https://joke-api.example.com/joke

You should receive a JSON payload with a random joke. That’s it—your first CapRover‑hosted app is live.

Deploying a Python Flask Application

CapRover isn’t limited to JavaScript. Let’s spin up a Flask microservice that serves a health check. Create a new folder locally:

mkdir flask-health && cd flask-health
python3 -m venv venv
source venv/bin/activate
pip install flask

Write app.py:

from flask import Flask, jsonify
app = Flask(__name__)

@app.route('/health')
def health():
    return jsonify(status='ok', uptime='{{ uptime }}')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

For the Dockerfile, we’ll use the official Python slim image and install the requirements:

FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]

Create requirements.txt containing:

flask

Initialize a Git repo, push to GitHub, then repeat the “Create New App” steps in the CapRover UI. This time, set the container port to 5000 in the app’s “App Configs” page, because Flask listens on that port by default.

After deployment, verify the health endpoint:

curl https://flask-health.example.com/health

You’ll see a JSON response confirming the service is up. This demonstrates CapRover’s language‑agnostic nature—just supply a Dockerfile and the platform does the rest.

Managing Databases and Persistent Storage

Most production apps need a database. CapRover provides a “One‑Click App” marketplace where you can launch PostgreSQL, MySQL, MongoDB, or Redis containers with a single click. Let’s add a PostgreSQL instance for our Node.js app.

  1. In the dashboard, go to One‑Click Apps → PostgreSQL and click “Install”.
  2. Give the instance a name (e.g., joke-db) and set a strong password.
  3. CapRover will create a new app, expose the default port 5432, and automatically generate environment variables like POSTGRES_USER, POSTGRES_PASSWORD, and POSTGRES_DB.

Now, back in the joke-api app settings, add those variables to the “Environment Variables” section. Update the Node.js code to use pg for a simple write‑once operation:

const { Pool } = require('pg');
const pool = new Pool({
  user: process.env.POSTGRES_USER,
  host: 'joke-db.captain.root',
  database: process.env.POSTGRES_DB,
  password: process.env.POSTGRES_PASSWORD,
  port: 5432,
});

app.post('/joke', async (req, res) => {
  const joke = req.body.joke;
  await pool.query('INSERT INTO jokes(text) VALUES($1)', [joke]);
  res.status(201).json({ message: 'Joke saved!' });
});

Remember to create the jokes table on first run (you can use a migration script or run manually via psql). CapRover also supports persistent volumes: enable a volume for the PostgreSQL app to ensure data survives container restarts.

Zero‑Downtime Deployments and Scaling

One of Heroku’s strongest selling points is its “release phase” that spins up a new dyno before terminating the old one. CapRover achieves the same effect with Docker’s built‑in rolling updates. When you push a new commit, CapRover builds a fresh image, tags it, and then replaces the old container while keeping the service endpoint alive.

To trigger a manual redeploy, use the CLI:

caprover apps redeploy -a joke-api

If you anticipate higher traffic, you can scale horizontally by increasing the number of container instances:

caprover apps setinstancecount -a joke-api -c 3

CapRover will spread the three instances across the Docker host, and the built‑in load balancer (a tiny Nginx reverse proxy) will round‑robin requests. For CPU‑intensive workloads, consider adding a second VPS and linking them via Docker Swarm, which CapRover supports out‑of‑the‑box.

Pro tip: Enable “Health Check Path” (e.g., /health) in the app settings. If an instance fails the health check, CapRover automatically removes it from the load balancer, preventing flaky deployments from affecting users.

Monitoring, Logs, and Debugging

CapRover surfaces container logs directly in the UI. Click on an app → “Logs” to stream real‑time output, just like heroku logs --tail. For deeper insights, you can attach a third‑party monitoring stack such as Prometheus + Grafana via the One‑Click Apps marketplace.

To view logs via the CLI:

caprover apps logs -a joke-api --tail 100

If you need to exec into a running container for debugging, use Docker’s exec command (CapRover runs on the same host):

docker exec -it $(docker ps -q -f "name=joke-api") sh

From inside, you can inspect environment variables, test database connections, or run a one‑off script without redeploying.

Security Best Practices

  • Automatic SSL: Always enable Let’s Encrypt for every public domain. CapRover renews certificates every 90 days without manual steps.
  • Least‑privilege users: Create separate CapRover users for CI/CD pipelines, granting only “Deploy” permissions.
  • Network isolation: Use Docker networks to keep database containers unreachable from the public internet. Only allow the app containers to communicate with them.
  • Regular updates: Run apt-get update && apt-get upgrade -y on the host weekly, and periodically pull the latest CapRover image with docker pull caprover/caprover.

Cost Comparison: Heroku vs. CapRover

Let’s break down a typical hobby project that runs a Node.js API, a PostgreSQL database, and a Redis cache. On Heroku’s free tier you would be limited to 550 dyno hours per month and no SSL for custom domains, forcing you to upgrade to the “Hobby” plan at $7 per dyno. Adding a Heroku Postgres hobby tier costs another $9/month, and Redis adds $15.

On CapRover, a single $5/month VPS can host all three containers with 2 GB RAM and 50 GB SSD. You pay the VPS fee, plus a negligible amount for bandwidth. In practice, you can run a 2‑core, 4 GB VPS for under $10/month and comfortably serve thousands of requests per day.

Beyond raw dollars, you gain control: you can upgrade the VPS, add more storage, or migrate to a different provider without rewriting deployment scripts. This flexibility is priceless for growing startups.

Real‑World Use Case: A Startup’s MVP

Acme Labs built a marketplace MVP using React for the frontend, a FastAPI backend, and PostgreSQL for transactions. They started on Heroku for rapid prototyping but hit the “Hobby” plan limits within weeks. By migrating to CapRover on a $8/month Linode, they achieved:

  • Zero‑cost SSL for app.acme.dev via Let’s Encrypt.
  • Two‑fold reduction in request latency (from 250 ms to 120 ms) thanks to a dedicated VPS.
  • Full control
Share this article