Apache CouchDB 3.5 on Ubuntu 22.04 on Azure User Guide
Overview
This image deploys Apache CouchDB 3.5 Core on Ubuntu 22.04, packaged by cloudimg for Microsoft Azure. CouchDB is a document oriented NoSQL database that stores JSON documents, exposes every operation over a RESTful HTTP API, ships with the Fauxton browser based admin UI, and supports multi master replication across data centres and offline first clients. CouchDB is written in Erlang and ships with the Erlang VM bundled in the Debian package, so there is no Java runtime on the image and no separate Python dependency.
The HTTP API, the Fauxton admin UI, and the replication protocol all listen on a single port, 5984. This is the simplest network exposure surface of any database image in the cloudimg catalogue and it is also the single biggest security consideration, because exposing port 5984 to the public internet exposes the admin UI as well as the data plane. Section 17 covers the security posture you want before putting customer traffic through the server.
The image is intended for teams that want a production posture single node CouchDB on day one without spending hours on packaging, systemd plumbing, admin bootstrap, or first database creation. It is not a clustered deployment across three or more nodes, it is not TLS encrypted out of the box, and it does not ship with a reverse proxy. The brand is lowercase cloudimg throughout this guide. All cloudimg URLs in this guide use the form https://www.cloudimg.co.uk.
Prerequisites
Before you deploy this image you need:
- A Microsoft Azure subscription where you can create resource groups, virtual networks, and virtual machines
- Azure role permissions equivalent to Contributor on the target resource group
- An SSH public key for first login to the admin user account
- A virtual network and subnet in the same region as the Azure Compute Gallery the image is published into, with an associated network security group
- The Azure CLI (
azversion 2.50 or later) installed locally if you intend to use the CLI deployment path in Section 2 - The cloudimg Apache CouchDB 3.5 offer enabled on your tenant in Azure Marketplace
Step 1: Deploy the Virtual Machine from the Azure Portal
Navigate to Marketplace in the Azure Portal, search for CouchDB, and select the cloudimg publisher entry. Click Create to begin the wizard.
On the Basics tab pick your subscription, resource group, and region. The region must match the region the Azure Compute Gallery publishes this image into (the default for the cloudimg offer is East US). The recommended VM size is Standard_B2s because CouchDB on Erlang is lightweight at idle and customers typically scale by data volume rather than CPU or memory. Create an admin username (the default azureuser is fine) and paste your SSH public key into the authentication section.
On the Disks tab the recommended OS disk type is Standard SSD. The CouchDB data directory lives at /opt/couchdb/data. If you expect your database to grow beyond a few gigabytes, attach a separate Premium SSD data disk now and follow Section 15 after the server is running to move the data directory across.
On the Networking tab attach the virtual network and subnet you prepared and pick an existing NSG or create a new one. Open port 22 for SSH from your management CIDR only, and open port 5984 from the CIDRs that host the applications that will talk to CouchDB. Do not open port 5984 to the public internet. That port serves the Fauxton admin UI as well as the REST API, so a wide open 5984 exposes the admin plane.
On the Management, Monitoring, and Advanced tabs the defaults are appropriate. Click Review + create, wait for validation to pass, then click Create. Deployment takes around two minutes.
Step 2: Deploy the Virtual Machine from the Azure CLI
If you prefer the command line, use the gallery image resource identifier as the source. The exact resource identifier is published on your Partner Center plan. A representative invocation:
RG="couchdb-prod"
LOCATION="eastus"
VM_NAME="couchdb-01"
ADMIN_USER="azureuser"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/couchdb-3-5-ubuntu-22-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az network vnet create \
--resource-group "$RG" \
--name couchdb-vnet \
--address-prefix 10.42.0.0/16 \
--subnet-name couchdb-subnet \
--subnet-prefix 10.42.1.0/24
az network nsg create --resource-group "$RG" --name couchdb-nsg
az network nsg rule create \
--resource-group "$RG" --nsg-name couchdb-nsg \
--name allow-ssh-mgmt --priority 100 \
--source-address-prefixes "<your-mgmt-cidr>" \
--destination-port-ranges 22 --access Allow --protocol Tcp
az network nsg rule create \
--resource-group "$RG" --nsg-name couchdb-nsg \
--name allow-couchdb-vnet --priority 110 \
--source-address-prefixes 10.42.0.0/16 \
--destination-port-ranges 5984 --access Allow --protocol Tcp
az vm create \
--resource-group "$RG" \
--name "$VM_NAME" \
--image "$GALLERY_IMAGE_ID" \
--size Standard_B2s \
--storage-sku StandardSSD_LRS \
--admin-username "$ADMIN_USER" \
--ssh-key-values "$SSH_KEY" \
--vnet-name couchdb-vnet --subnet couchdb-subnet --nsg couchdb-nsg \
--public-ip-sku Standard --location "$LOCATION"
Step 3: Connect via SSH
Once the VM is running, connect over SSH using the admin user you chose:
ssh azureuser@<vm-ip>
Replace <vm-ip> with the public IP address shown in the Azure Portal or returned by az vm show -g "$RG" -n "$VM_NAME" --query publicIps -o tsv.
Step 4: Retrieve the Admin Password
The image ships with a first boot service that generates a unique admin password per VM on first boot, writes it to /opt/couchdb/etc/local.d/10-admins.ini (which CouchDB immediately rewrites as a pbkdf2 hash when the daemon starts), and writes the plain text password to /stage/scripts/couchdb-credentials.log with mode 600 root only. Read it once, store it in your secrets manager, and reference it from the shell for the rest of this guide:
sudo cat /stage/scripts/couchdb-credentials.log
export PASSWORD="$(sudo awk -F= '/^password=/ {print $2}' /stage/scripts/couchdb-credentials.log)"
echo "admin password length: ${#PASSWORD}"
The credentials file also records the default port and a one line sample_connect command you can copy and paste. Every subsequent bash command in this guide assumes $PASSWORD is exported.
Step 5: Verify the Server is Healthy
The CouchDB welcome endpoint returns a JSON document that confirms the server is running and reports its version. The _up endpoint is a dedicated health probe. Both require HTTP Basic authentication because the image ships with require_valid_user = true:
curl -s -u "admin:$PASSWORD" http://localhost:5984/
curl -s -u "admin:$PASSWORD" http://localhost:5984/_up
The welcome response reports "couchdb":"Welcome" and the installed version. The _up response returns {"status":"ok","seeds":{}}.
Step 6: Server Components
| Component | Version | Source |
|---|---|---|
| Apache CouchDB | 3.5.x | apache.jfrog.io/artifactory/couchdb-deb jammy main |
| Erlang VM | bundled in the CouchDB .deb | Apache CouchDB package |
| Ubuntu | 22.04 LTS | Azure Marketplace base image |
| cloudimg overrides | 20-cloudimg.ini |
baked into the image |
CouchDB ships with the Erlang VM statically bundled inside /opt/couchdb so the image has no system Erlang dependency and there is no JDK on the image. There is no separate backend database, no message broker, and no second port, so there is nothing else to lifecycle.
Step 7: Filesystem Layout
CouchDB is self contained under /opt/couchdb. The main directories and files the image exposes are:
/opt/couchdb/bin/couchdb— the CouchDB launcher script/opt/couchdb/etc/local.ini— primary configuration file, defaults shipped with the package/opt/couchdb/etc/local.d/10-admins.ini— admin credentials (plaintext at first boot, hashed after daemon startup)/opt/couchdb/etc/local.d/20-cloudimg.ini— cloudimg overrides (port, bind address, auth mode)/opt/couchdb/etc/vm.args— Erlang VM arguments (node name, cookie)/opt/couchdb/data/— all database files and indexes/opt/couchdb/var/log/couchdb.log— structured log output/stage/scripts/couchdb-credentials.log— per VM admin password, mode 600 root only/etc/systemd/system/couchdb.service.d/10-firstboot-order.conf— systemd drop in ordering couchdb.service after the firstboot unit
Step 8: Start, Stop, and Check Status
CouchDB runs as the systemd service couchdb.service. The firstboot unit is couchdb-firstboot.service and is skipped on every boot after the first because its ConditionPathExists sentinel lives at /opt/couchdb/data/.firstboot-done. You interact with the main service using systemctl:
sudo systemctl status --no-pager couchdb
sudo systemctl is-active couchdb
To restart the server after editing configuration:
sudo systemctl restart couchdb
The service runs as the couchdb system user, owns /opt/couchdb, and writes its logs to /opt/couchdb/var/log/couchdb.log.
Step 9: Open the Fauxton Admin UI
CouchDB ships with Fauxton, a browser based admin UI that runs on the same port as the REST API, 5984. Once you have opened port 5984 from your management CIDR in the NSG (Section 1 and Section 2), open the following URL in your browser:
http://<vm-ip>:5984/_utils/
Log in with the admin user and the password from /stage/scripts/couchdb-credentials.log. Fauxton exposes database browsing, document editing, Mango query building, and the replication control panel. Because Fauxton speaks the same REST API as every other client, anything you can do in Fauxton you can also do with curl, and vice versa.
Step 10: Create a Database and a Document
Every CouchDB write is an HTTP request. Create a database with PUT, write a JSON document with POST, and list documents with GET:
curl -s -u "admin:$PASSWORD" -X PUT http://localhost:5984/mydb
curl -s -u "admin:$PASSWORD" -X POST http://localhost:5984/mydb \
-H 'Content-Type: application/json' \
-d '{"name":"test","value":42}'
curl -s -u "admin:$PASSWORD" http://localhost:5984/mydb/_all_docs?include_docs=true
The first call returns {"ok":true}. The second returns {"ok":true,"id":"<generated-uuid>","rev":"1-<hash>"}. The third returns a JSON document that lists every document in the database with its _id, _rev, and (because of include_docs=true) the document body.
Step 11: Query with Mango
Mango is CouchDB's declarative query language. It uses the same JSON selector syntax as MongoDB and returns results via the /_find endpoint. Without an explicit index Mango falls back to a full scan and CouchDB prints a warning, which is fine for ad hoc queries over small databases:
curl -s -u "admin:$PASSWORD" -X POST http://localhost:5984/mydb/_find \
-H 'Content-Type: application/json' \
-d '{"selector":{"name":"test"}}'
The response contains a docs array with every document matching the selector. For production use, create an index with POST /<db>/_index so Mango can plan against it.
Step 12: Create Per Application Users
The admin account should never be used by application traffic. Create a per application user by writing a user document into the built in _users database, then grant the user read or write access to a database by PUTting _security on the target database:
curl -s -u "admin:$PASSWORD" -X PUT http://localhost:5984/_users/org.couchdb.user:app1 \
-H 'Content-Type: application/json' \
-d '{"name":"app1","password":"change-me-in-your-secrets-manager","roles":[],"type":"user"}'
curl -s -u "admin:$PASSWORD" -X PUT http://localhost:5984/mydb/_security \
-H 'Content-Type: application/json' \
-d '{"admins":{"names":[],"roles":[]},"members":{"names":["app1"],"roles":[]}}'
From the application side, connect using HTTP Basic auth exactly as the admin does, just with the new credentials:
curl -s -u "app1:change-me-in-your-secrets-manager" http://localhost:5984/mydb/_all_docs?limit=1
CouchDB hashes the stored password on daemon restart, so the plain text in the PUT body above is not persisted.
Step 13: Rotate the Admin Password
To rotate the admin password, PUT the new value to the admin config endpoint. CouchDB hashes it on the next daemon config read and overwrites /opt/couchdb/etc/local.d/10-admins.ini. After the rotation, update /stage/scripts/couchdb-credentials.log so future readers see the current value:
NEW_PASSWORD="$(head -c 32 /dev/urandom | base64 | tr -d '/+=' | head -c 24)"
curl -s -u "admin:$PASSWORD" -X PUT \
http://localhost:5984/_node/_local/_config/admins/admin \
-H 'Content-Type: application/json' \
--data "\"$NEW_PASSWORD\""
sudo sed -i "s|^password=.*|password=$NEW_PASSWORD|" /stage/scripts/couchdb-credentials.log
export PASSWORD="$NEW_PASSWORD"
curl -s -u "admin:$PASSWORD" http://localhost:5984/_up
The final /_up call confirms the new password works. Store the new value in your secrets manager immediately.
Step 14: Configure Replication
CouchDB replicates by writing a document to the _replicator database that describes the source, target, and policy. A pull replication from a remote peer into a local database looks like this:
curl -s -u "admin:$PASSWORD" -X POST http://localhost:5984/_replicator \
-H 'Content-Type: application/json' \
-d '{"_id":"pull-from-peer","source":"http://remote.example.com:5984/mydb","target":"http://admin:local@localhost:5984/mydb","continuous":true}'
Replace the source URL with your peer and substitute peer credentials as needed. Replication state is visible in Fauxton under Replication, or via GET /_scheduler/docs.
Step 15: Move Data to an Attached Premium Disk
The default image keeps /opt/couchdb/data on the OS disk. For larger workloads, attach a Premium SSD, format it, mount it at /opt/couchdb/data, and restart the service:
sudo systemctl stop couchdb
sudo mkfs.ext4 -F /dev/sdc
sudo mkdir -p /mnt/couchdb-data
sudo mount /dev/sdc /mnt/couchdb-data
sudo rsync -aHAX /opt/couchdb/data/ /mnt/couchdb-data/
echo "/dev/sdc /opt/couchdb/data ext4 defaults,noatime 0 2" | sudo tee -a /etc/fstab
sudo mv /opt/couchdb/data /opt/couchdb/data.old
sudo mkdir /opt/couchdb/data
sudo mount /opt/couchdb/data
sudo chown -R couchdb:couchdb /opt/couchdb/data
sudo systemctl start couchdb
Verify the new mount with df -h /opt/couchdb/data and confirm the service came back with systemctl status couchdb.
Step 16: Troubleshooting
If CouchDB is not reachable, collect the following in order:
sudo systemctl status --no-pager couchdb
sudo journalctl -u couchdb --no-pager -n 50
sudo tail -n 50 /opt/couchdb/var/log/couchdb.log
sudo ss -tln | grep 5984
Common failure modes and their signatures:
couchdb.service: Main process exited, code=exited, status=1/FAILUREwithNo Admin Account Found, aborting startupin couchdb.log — the firstboot service did not write /opt/couchdb/etc/local.d/10-admins.ini before couchdb.service started. Runsudo systemctl start couchdb-firstbootmanually and then restart couchdb.curlreturnsUnauthorized— the password on disk has been rotated out from under you. Re export$PASSWORDfrom /stage/scripts/couchdb-credentials.log.curlhangs with no response — port 5984 is not bound. Check the bind_address in /opt/couchdb/etc/local.d/20-cloudimg.ini and confirm the NSG rule.- Replication documents show
errorstate in /_scheduler/docs — the source URL is unreachable or the credentials in the replication document are wrong.
Step 17: Security Recommendations
CouchDB serves the REST API, the Fauxton admin UI, and the replication protocol all on port 5984. The single port model means the security posture of the port matters more than for most databases. Before taking customer traffic, apply the following:
- Never expose port 5984 to the public internet. The Fauxton admin UI is on that port. Restrict the NSG rule to your application VNets.
- Rotate the admin password using Section 13 and store the new value in your secrets manager. The file /stage/scripts/couchdb-credentials.log should not be considered your source of truth after first boot.
- Create a per application user with Section 12 and disable the admin account from application traffic. Only admin operations (create database, change config) should use the admin credential.
- Terminate TLS with a reverse proxy. nginx in front of CouchDB with a Let's Encrypt certificate gives you HTTPS without changing the CouchDB configuration. Customer applications talk to the proxy, the proxy speaks to localhost:5984.
- Disable CORS unless you serve Fauxton to browsers from another origin.
enable_cors = falseis the cloudimg default and is correct for server to server traffic. - Back up /opt/couchdb/data regularly. CouchDB's append only file format tolerates tarball snapshots of a running database, but for point in time consistency stop the service, snapshot the disk, and restart.
Step 18: Support and Licensing
The image and the guide are published by cloudimg. Apache CouchDB is licensed under the Apache License 2.0 and is maintained by the Apache Software Foundation. cloudimg does not redistribute a modified CouchDB binary; the image uses the upstream package from apache.jfrog.io and layers only the cloudimg overrides in /opt/couchdb/etc/local.d/20-cloudimg.ini and the firstboot helper under /usr/local/sbin.
For cloudimg packaging issues, image defects, or questions specific to this Azure image, contact support at https://www.cloudimg.co.uk. For CouchDB product questions, tuning, or bug reports, use the Apache CouchDB community channels at https://couchdb.apache.org.
The brand is lowercase cloudimg throughout. This guide is maintained at https://www.cloudimg.co.uk/guides/couchdb-3-5-on-ubuntu-22-04-azure/.