Ca
Developer Tools Azure

CapRover on Ubuntu 24.04 on Azure User Guide

| Product: CapRover on Ubuntu 24.04 LTS on Azure

Overview

CapRover is an open source, self hosted platform as a service. It turns a single server into your own application platform: point it at a Git repository, a Dockerfile or a container image and CapRover builds it, runs it under Docker, wires it into its built in nginx reverse proxy and gives it a routable URL. A one click app library covers dozens of common services such as databases, caches, queues and content management systems.

The cloudimg image runs CapRover from the vendor's official caprover/caprover container, pinned by digest, on a single node Docker Swarm. CapRover manages its own nginx (captain-nginx) on ports 80 and 443 and publishes its admin dashboard on port 3000.

Portable by design: a Docker Swarm is bound to the address of the node that created it, so no swarm is baked into this image. caprover-firstboot.service creates the swarm freshly on your virtual machine at first boot, advertising your machine's own private address. That is what makes the image portable instead of carrying a build server's identity into your environment.

Secure by default: CapRover's published default dashboard password (captain42) is never present in this image. A unique password is generated on each VM at first boot, written 0600 root:root to /root/caprover-credentials.txt, and first boot verifies both that your password works and that the upstream default is refused before it completes.

What is included:

  • CapRover 1.14.2 (Apache-2.0) from the official caprover/caprover image, pinned by digest
  • Single node Docker Swarm created on first boot, advertising your VM's own address
  • captain-captain (dashboard and API), captain-nginx (reverse proxy) and captain-certbot (HTTPS) swarm services
  • caprover.service, an idempotent reconciler that brings the platform back after every reboot
  • Per VM dashboard password generated at first boot, stored 0600 root:root
  • caprover-firstboot.service for first boot swarm creation and password generation
  • Docker Engine runtime, with the CapRover and nginx images pre-pulled
  • Ubuntu 24.04 LTS base, fully patched
  • 24/7 cloudimg support, 24h response SLA

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet with a subnet. Recommended VM size: Standard_B2s. CapRover itself is light, but size the VM for the applications you intend to run on it.

Step 1: Deploy from the Azure Portal

Search the Marketplace for CapRover on Ubuntu 24.04, choose your VM size, and attach a network security group that allows:

Port Protocol Purpose
22 TCP SSH, from your management network only
80 TCP Your deployed applications, and Let's Encrypt HTTP validation
443 TCP Your deployed applications over HTTPS
3000 TCP The CapRover dashboard, from your management network only

Port 3000 is the admin dashboard and should be restricted to the networks you administer from. Once you have set a root domain you can reach the dashboard at captain.<your-root-domain> over 80/443 and close 3000 entirely.

If you later expand to a multi node swarm, the nodes additionally need 2377/TCP (cluster management), 7946/TCP and 7946/UDP (node discovery) and 4789/UDP (overlay network) open between themselves only.

Step 2: Deploy from the Azure CLI

RG="caprover-prod"; LOCATION="eastus"; VM_NAME="caprover-01"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/caprover-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az network vnet create -g "$RG" --name caprover-vnet --address-prefix 10.91.0.0/16 --subnet-name caprover-subnet --subnet-prefix 10.91.1.0/24
az network nsg create -g "$RG" --name caprover-nsg
az network nsg rule create -g "$RG" --nsg-name caprover-nsg --name allow-ssh --priority 100 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp
az network nsg rule create -g "$RG" --nsg-name caprover-nsg --name allow-dashboard --priority 110 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 3000 --access Allow --protocol Tcp
az network nsg rule create -g "$RG" --nsg-name caprover-nsg --name allow-web --priority 120 \
  --source-address-prefixes "*" --destination-port-ranges 80 443 --access Allow --protocol Tcp
az vm create -g "$RG" --name "$VM_NAME" --image "$GALLERY_IMAGE_ID" \
  --size Standard_B2s --storage-sku StandardSSD_LRS \
  --admin-username azureuser --ssh-key-values "$SSH_KEY" \
  --vnet-name caprover-vnet --subnet caprover-subnet --nsg caprover-nsg --public-ip-sku Standard

Step 3: Connect via SSH

ssh azureuser@<vm-ip>

First boot creates the swarm and bootstraps the platform. Allow about a minute after the VM reports running before the dashboard answers.

Step 4: Verify the platform is running

CapRover runs as Docker Swarm services rather than as individual systemd units. caprover.service is the reconciler that brings them up and keeps them up across reboots.

systemctl is-active docker caprover
sudo docker service ls --format 'table {{.Name}}\t{{.Replicas}}\t{{.Image}}'
sudo ss -tlnp | grep -E ':80|:443|:3000'

Both units report active, the captain-captain, captain-nginx and captain-certbot services each report 1/1 replicas, and the host is listening on 80, 443 and 3000.

docker and caprover services reporting active, the captain-captain, captain-nginx and captain-certbot swarm services each running 1 of 1 replicas, and the host listening on ports 80, 443 and 3000

Step 5: Retrieve your per VM dashboard password

The password was generated uniquely for this VM at first boot. It is stored root only, and it is the only password that works: CapRover's published default is rejected.

sudo stat -c '%n perms=%a owner=%U:%G' /root/caprover-credentials.txt
sudo sed -E 's/(CAPROVER_PASSWORD=).*/\1************/' /root/caprover-credentials.txt
curl -s -X POST -H 'Content-Type: application/json' -H 'x-namespace: captain' \
  -d '{"password":"captain42"}' http://127.0.0.1:3000/api/v2/login | jq -r '.description'

The credentials file is 600 root:root, it holds a unique per VM CAPROVER_PASSWORD (masked above), and CapRover's upstream default password returns Invalid credentials.

Read the real password with sudo grep CAPROVER_PASSWORD /root/caprover-credentials.txt, and change it from the dashboard under Settings after your first sign in.

The credentials file stored 600 root:root with a unique per VM password masked, and CapRover's upstream default password captain42 returning Invalid credentials

Step 6: Sign in to the dashboard

Browse to http://<vm-ip>:3000/ and enter the password from Step 5. There is no username field: CapRover has a single administrator sign in that uses a password only.

The CapRover dashboard login screen asking for the administrator password, served on port 3000

You can confirm the same credential works from the command line:

curl -s -X POST -H 'Content-Type: application/json' -H 'x-namespace: captain' \
  -d '{"password":"<caprover-password>"}' http://127.0.0.1:3000/api/v2/login | jq -r '.description'

This returns Login succeeded.

Step 7: Set your root domain

CapRover gives every application a subdomain of one root domain, so this is the first thing to configure. Point a wildcard DNS record at your VM's public IP:

*.apps.example.com.   A   <vm-public-ip>

Then set that root domain in the dashboard under Settings > Root Domain, or from the command line. CapRover verifies that the domain really resolves to this machine before accepting it.

TOKEN=$(curl -s -X POST -H 'Content-Type: application/json' -H 'x-namespace: captain' \
  -d '{"password":"<caprover-password>"}' http://127.0.0.1:3000/api/v2/login | jq -r '.data.token')
CAPROVER_IP=$(hostname -I | awk '{print $1}')
curl -s -X POST -H 'Content-Type: application/json' -H 'x-namespace: captain' \
  -H "x-captain-auth: $TOKEN" \
  -d "{\"rootDomain\":\"${CAPROVER_IP}.nip.io\"}" \
  http://127.0.0.1:3000/api/v2/user/system/changerootdomain | jq -r '.description'

This returns Root domain changed.

The command above uses nip.io, a public wildcard DNS service that resolves anything.10.0.0.4.nip.io to 10.0.0.4. It needs no DNS setup at all, so it is the quickest way to try the platform end to end. Use your own wildcard domain for production — nip.io is a third party convenience, not a production dependency, and Let's Encrypt certificates need a domain you control.

The CapRover Root Domain Configurations panel with the root domain set to 10.0.0.10.nip.io and the Enable HTTPS and Force HTTPS controls beside it

Step 8: Deploy your first application

In the dashboard, open Apps, enter a name and select Create New App, then deploy to it from a Git repository, an uploaded tarball, a Dockerfile or an existing image. The One Click Apps tab installs pre packaged services such as PostgreSQL, MySQL, Redis, MongoDB, WordPress, n8n and Ghost in a single form.

The equivalent from the command line creates an app called hello and deploys the nginx image to it:

TOKEN=$(curl -s -X POST -H 'Content-Type: application/json' -H 'x-namespace: captain' \
  -d '{"password":"<caprover-password>"}' http://127.0.0.1:3000/api/v2/login | jq -r '.data.token')
curl -s -X POST -H 'Content-Type: application/json' -H 'x-namespace: captain' -H "x-captain-auth: $TOKEN" \
  -d '{"appName":"hello","hasPersistentData":false}' \
  http://127.0.0.1:3000/api/v2/user/apps/appDefinitions/register | jq -r '.description'
curl -s -X POST -H 'Content-Type: application/json' -H 'x-namespace: captain' -H "x-captain-auth: $TOKEN" \
  -d '{"captainDefinitionContent":"{\"schemaVersion\":2,\"imageName\":\"nginx:1.31\"}","gitHash":""}' \
  "http://127.0.0.1:3000/api/v2/user/apps/appData/hello?detached=1" | jq -r '.description'
for i in $(seq 1 24); do
  BUILDING=$(curl -s -H 'x-namespace: captain' -H "x-captain-auth: $TOKEN" \
    http://127.0.0.1:3000/api/v2/user/apps/appData/hello | jq -r '.data.isAppBuilding')
  [ "$BUILDING" = "false" ] && break
  sleep 5
done
echo "build finished: isAppBuilding=$BUILDING"

CapRover registers the app, starts the deployment and reports Deploy is started, then the build completes.

The CapRover Apps view listing the deployed hello application with an instance count of 1 and its last deployed timestamp

Step 9: Confirm the application is live

Your app now runs as its own swarm service and is routed by CapRover's nginx on port 80.

CAPROVER_IP=$(hostname -I | awk '{print $1}')
sudo docker service ls --filter name=srv-captain--hello --format 'table {{.Name}}\t{{.Replicas}}\t{{.Image}}'
curl -s -o /dev/null -w 'app HTTP %{http_code}\n' "http://hello.${CAPROVER_IP}.nip.io/"
curl -s "http://hello.${CAPROVER_IP}.nip.io/" | sed -n '1,4p'

The service reports 1/1 replicas and the application answers HTTP 200 through CapRover's reverse proxy, returning the nginx welcome page.

The deployed hello application running as the srv-captain--hello swarm service and answering HTTP 200 with its page content through CapRover's nginx reverse proxy

The application's own page in the dashboard shows its deployment history, instance count, environment variables, persistent directories and HTTP settings.

The CapRover application detail view for the deployed hello app, showing it is publicly available at http://hello.10.0.0.10.nip.io with an Enable HTTPS control

Step 10: Enable HTTPS

Once your root domain resolves to this VM and ports 80 and 443 are open, enable HTTPS from the dashboard. Under Settings, provide an email address and select Enable HTTPS for the dashboard domain; for each application, open its HTTP Settings and select Enable HTTPS. CapRover requests and renews Let's Encrypt certificates automatically through its captain-certbot service, and can force HTTP to redirect to HTTPS per application.

Let's Encrypt validates over port 80, so leave 80 open even after your traffic moves to 443.

Step 11: Verify the platform build

sudo docker image inspect caprover/caprover:1.14.2 --format '{{index .RepoDigests 0}}'
sudo docker info --format 'swarm={{.Swarm.LocalNodeState}} nodes={{.Swarm.Nodes}} managers={{.Swarm.Managers}}'
curl -s -o /dev/null -w 'dashboard HTTP %{http_code}\n' http://127.0.0.1:3000/
apt-mark showhold | wc -l

The image digest matches the pinned upstream release, the swarm is active with one node acting as manager, the dashboard returns HTTP 200, and there are zero held packages, so the OS keeps receiving security updates.

The pinned CapRover image digest, an active single node swarm with one manager, the dashboard returning HTTP 200, and no held packages in the OS patch baseline

Managing the platform

sudo systemctl status caprover --no-pager -n 0
sudo docker service ps captain-captain --filter desired-state=running --format 'table {{.Name}}\t{{.CurrentState}}'
sudo journalctl -u caprover --no-pager -n 8

The task list shows the running replica of the dashboard service, and the reconciler's own journal follows. Follow live application logs with sudo docker service logs -f captain-captain. caprover.service is an idempotent reconciler: it brings the swarm services up if they are missing and returns immediately if they are already running, which is what restores the platform after a reboot. Restart the platform with sudo systemctl restart caprover, and follow live logs with sudo docker service logs -f captain-captain.

All CapRover state lives under /captain on the OS disk, so snapshotting the OS disk backs up your platform configuration, application definitions and certificates. Your applications' persistent volumes live under /var/lib/docker/volumes/.

Support

cloudimg provides 24/7 support for this image via email and live chat: deployment, DNS and wildcard domain setup, HTTPS, one click apps, upgrades and performance tuning. Email support@cloudimg.co.uk.

This is a repackaged open source software product with additional charges for cloudimg support services. CapRover is a trademark of its respective owner. All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.