CapRover is an open-source platform as a service that runs on Docker Swarm, with an Nginx reverse proxy, automatic Let's Encrypt certificates and a web dashboard to manage your apps. You push code with a small captain-definition file and CapRover builds and runs it as a container. In this tutorial you will install CapRover on Ubuntu 24.04, configure a wildcard domain with HTTPS, deploy an application from your workstation with the CapRover CLI, connect it to a one-click PostgreSQL database and add persistent storage.

Prerequisites

To follow this tutorial you need:

  • A fresh server running Ubuntu 24.04 LTS with at least 1 vCPU and 1 GB of RAM, for example a CubePath VPS. Use 2 GB or more if you build apps on the server, since Docker builds need memory.
  • A non-root user with sudo privileges.
  • A domain name where you can create a wildcard DNS record. This tutorial uses apps.your_domain as CapRover's root domain.
  • Node.js 18 or later and Git installed on your local workstation, to run the CapRover CLI.
  • No other service listening on ports 80 or 443 on the server.

Step 1 - Creating the wildcard DNS record

CapRover gives every app a subdomain of its root domain (myapp.apps.your_domain) and serves its own dashboard at captain.apps.your_domain. A single wildcard record covers all of them. In your DNS provider, create:

TypeNameValue
A*.appsyour_server_ip

Check that any subdomain resolves to your server:

dig +short test.apps.your_domain
your_server_ip

Step 2 - Installing Docker Engine

CapRover runs on Docker. Install Docker Engine from Docker's official repository rather than the docker.io Ubuntu package, so you get current releases.

Add Docker's signing key:

sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

Add the repository:

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Install Docker Engine:

sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Verify that the service is running:

sudo systemctl is-active docker
active

Step 3 - Opening the firewall ports

CapRover needs HTTP and HTTPS, port 3000 for the initial setup, port 996 for its internal Docker registry and the Docker Swarm ports 7946, 4789 and 2377 if you ever add more nodes. Open them with UFW:

sudo ufw allow OpenSSH
sudo ufw allow 80,443,3000,996,7946,4789,2377/tcp
sudo ufw allow 7946,4789,2377/udp
sudo ufw enable

Check the result:

sudo ufw status
Status: active

To                                  Action      From
--                                  ------      ----
OpenSSH                             ALLOW       Anywhere
80,443,3000,996,7946,4789,2377/tcp  ALLOW       Anywhere
7946,4789,2377/udp                  ALLOW       Anywhere
...

Step 4 - Installing CapRover

CapRover installs with a single container that initializes Docker Swarm and starts CapRover as Swarm services. Setting ACCEPTED_TERMS=true accepts CapRover's terms of use, and /captain on the host stores all of its data.

sudo docker run -p 80:80 -p 443:443 -p 3000:3000 \
  -e ACCEPTED_TERMS=true \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /captain:/captain \
  caprover/caprover

Wait until the output says the installation is complete, then press CTRL+C if the command does not return by itself. CapRover keeps running as a Swarm service. Confirm it:

sudo docker service ls --format 'table {{.Name}}\t{{.Replicas}}'
NAME              REPLICAS
captain-captain   1/1
captain-nginx     1/1
...

At this point the dashboard answers on http://your_server_ip:3000 with the default password captain42. Do not leave it like this; the next step changes the password and enables HTTPS.

Step 5 - Running the initial setup with the CLI

The caprover serversetup command sets the root domain, changes the default password, enables HTTPS for the dashboard and saves the server as a named machine for later deployments. Run the following on your local workstation, not on the server.

Install the CLI:

npm install -g caprover

Start the setup:

caprover serversetup

Answer the prompts:

  • IP address of your server: your_server_ip
  • Current CapRover password: captain42
  • CapRover server root domain: apps.your_domain
  • New CapRover password: a strong password of your own
  • Valid email address: used for Let's Encrypt expiry notices
  • Name for this machine: for example production

When it finishes, the dashboard is served at https://captain.apps.your_domain. Open that address, log in with your new password and confirm the padlock in the browser. You can also list the machines the CLI knows about:

caprover list
Logged in CapRover Machines:

>> production at https://captain.apps.your_domain

Step 6 - Deploying an application

Every CapRover app needs a captain-definition file at the root of the repository. It tells CapRover how to build the image.

First create the app on the server. In the dashboard, go to Apps, type myapp as the app name and click Create New App.

On your workstation, in the root of your project, create the definition file:

nano captain-definition

Point it at the Dockerfile in the same directory:

{
  "schemaVersion": 2,
  "dockerfilePath": "./Dockerfile"
}

A minimal Dockerfile for a Node.js app listening on port 3000 looks like this:

FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

CapRover sends traffic to port 80 inside the container by default. Because this app listens on 3000, open the app in the dashboard, go to HTTP Settings, set Container HTTP Port to 3000 and click Save & Update.

The CLI deploys the latest commit of the current branch, so commit your changes first:

git add captain-definition Dockerfile
git commit -m "Add CapRover deployment files"

Deploy:

caprover deploy -n production -a myapp -b main

The CLI uploads the code, streams the build log and reports success:

...
Deployed successfully myapp

Verify from your workstation:

curl -I http://myapp.apps.your_domain
HTTP/1.1 200 OK
...

Step 7 - Enabling HTTPS for the app

In the dashboard, open myapp and go to HTTP Settings:

  1. Click Enable HTTPS. CapRover requests a Let's Encrypt certificate for myapp.apps.your_domain.
  2. Turn on Force HTTPS by redirecting all HTTP traffic to HTTPS and click Save & Update.

To serve the app on your own domain, create an A record for www.example.com pointing to your_server_ip, add it under Connect New Domain, then click Enable HTTPS next to it.

Check the certificate:

curl -sI https://myapp.apps.your_domain | head -n 1
HTTP/2 200

Step 8 - Adding a PostgreSQL database

CapRover's One-Click Apps/Databases menu deploys common services such as PostgreSQL, MySQL, Redis or WordPress from templates.

  1. In Apps, click One-Click Apps/Databases and search for PostgreSQL.
  2. Set the app name to postgres, choose a strong password and a database name such as myapp, then click Deploy.

Apps in CapRover share an overlay network and reach each other through the hostname srv-captain--<app-name>. Add the connection string to your app under App Configs, Environmental Variables:

DATABASE_URL=postgresql://postgres:your_strong_password@srv-captain--postgres:5432/myapp

Click Save & Update to restart the app with the new variable. The database is not exposed to the internet: only other CapRover apps can reach it.

Step 9 - Adding persistent storage

Containers are recreated on every deploy, so anything written inside them is lost. To keep uploaded files, the app must be created with persistent data enabled. When you create an app that needs storage, check Has Persistent Data in the Apps screen before clicking Create New App.

Then, in App Configs, add a persistent directory:

  • Path in App: /app/uploads
  • Label: myapp-uploads (CapRover creates a Docker volume with this name)

Click Save & Update. Verify the volume exists on the server:

sudo docker volume ls | grep myapp-uploads
local     captain--myapp-uploads

Troubleshooting

The build fails. Read the build log shown by caprover deploy or in the app's Deployment tab. Missing captain-definition, uncommitted files and running out of memory or disk (free -h, df -h /) are the usual causes.

The app returns "502 Bad Gateway". The Container HTTP Port does not match the port the process listens on, or the app listens on 127.0.0.1. Check the app logs in the dashboard or on the server:

sudo docker service logs srv-captain--myapp --tail 50

Let's Encrypt fails for the root domain or an app. The wildcard record must resolve to this server and port 80 must be reachable from the internet. Check the CapRover logs:

sudo docker service logs captain-captain --since 30m

Conclusion

You installed CapRover on Ubuntu 24.04, secured the dashboard with a wildcard domain and HTTPS, deployed an application with the CLI and connected it to a one-click PostgreSQL database with persistent storage. Next, you can enable webhook deployments from GitHub or GitLab in the app's Deployment tab, schedule backups of your database volumes, or add worker nodes from the Cluster page to spread apps across several servers.