How to install CouchDB

Install CouchDB on Debian Buster.

Initial information

CouchDB is using port 5984 for standard communication. Do not expose other ports like 5986 (node and shard management) and 4369 (Erlang port mapper daemon) to the internet.

This is a single-node setup as it is more than enough to get started with CouchDB. I will mention various configuration options, but you can ignore these related to the clustered setup.

I am not using ssl configuration as service is always behind HAProxy load balancer.

Prerequisites

Install required packages.

$ sudo apt-get install curl gnupg apt-transport-https
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
  dirmngr gnupg-l10n gnupg-utils gpg gpg-agent gpg-wks-client gpg-wks-server gpgconf gpgsm libcurl4 libksba8 libnpth0
Suggested packages:
  dbus-user-session pinentry-gnome3 tor parcimonie xloadimage scdaemon
The following NEW packages will be installed:
  apt-transport-https curl dirmngr gnupg gnupg-l10n gnupg-utils gpg gpg-agent gpg-wks-client gpg-wks-server gpgconf gpgsm libcurl4 libksba8 libnpth0
0 upgraded, 15 newly installed, 0 to remove and 1 not upgraded.
Need to get 7720 kB of archives.
[...]

Install CouchDB

Import CouchDB public key using curl.

$ curl --silent https://couchdb.apache.org/repo/bintray-pubkey.asc | sudo apt-key add -
OK

Define CouchDB repository.

$ echo "deb https://apache.bintray.com/couchdb-deb buster main" | sudo tee /etc/apt/sources.list.d/couchdb.list
deb https://apache.bintray.com/couchdb-deb buster main

Update package index.

$ sudo apt-get update
Hit:1 http://security.debian.org/debian-security buster/updates InRelease
Hit:2 http://deb.debian.org/debian buster InRelease                        
Ign:3 https://apache.bintray.com/couchdb-deb buster InRelease              
Get:4 https://apache.bintray.com/couchdb-deb buster Release [3558 B]
Get:5 https://apache.bintray.com/couchdb-deb buster Release.gpg [821 B]
Get:6 https://apache.bintray.com/couchdb-deb buster/main amd64 Packages [2272 B]
Fetched 6651 B in 1s (11.9 kB/s)   
Reading package lists... Done

Set CouchDB configuration to none as it is better suited for automation.

$ cat << EOF | sudo debconf-copydb pipe configdb --config=Name:pipe
Name: couchdb/mode
Template: couchdb/mode
Value: none
Owners: couchdb
Flags: seen
EOF

Ignore use of uninitialized value errors. Template will be provided by a CouchDB package.

Use of uninitialized value $template in exists at /usr/share/perl5/Debconf/Template.pm line 86.
Use of uninitialized value $item in exists at /usr/share/perl5/Debconf/DbDriver/Cache.pm line 40.
Use of uninitialized value $template in exists at /usr/share/perl5/Debconf/Template.pm line 86.
Use of uninitialized value $item in exists at /usr/share/perl5/Debconf/DbDriver/Cache.pm line 40.
Name: couchdb/mode
Template: couchdb/mode
Value: none
Owners: couchdb
Flags: seen

Verify initial CouchDB configuration.

$ sudo debconf-copydb configdb stdout \
    --config=Name:stdout              \
    --config=Driver:Pipe              \
    --config=InFd:none                \
    --pattern='^couchdb'
Name: couchdb/mode
Template: couchdb/mode
Value: none
Owners: couchdb
Flags: seen

Install CouchDB.

$ sudo apt-get install couchdb
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following additional packages will be installed:
  couch-libmozjs185-1.0 libnspr4 python3-progressbar
The following NEW packages will be installed:
  couch-libmozjs185-1.0 couchdb libnspr4 python3-progressbar
0 upgraded, 4 newly installed, 0 to remove and 1 not upgraded.
Need to get 23.3 MB of archives.
After this operation, 44.6 MB of additional disk space will be used.
Get:1 http://deb.debian.org/debian buster/main amd64 libnspr4 amd64 2:4.20-1 [112 kB]
Get:2 https://apache.bintray.com/couchdb-deb buster/main amd64 couch-libmozjs185-1.0 amd64 1.8.5-1.0.0+couch-2~buster [1180 kB]
Get:3 http://deb.debian.org/debian buster/main amd64 python3-progressbar all 2.5-1 [10.3 kB]
Get:4 https://apache.bintray.com/couchdb-deb buster/main amd64 couchdb amd64 2.3.1-1~buster [22.0 MB]
Fetched 23.3 MB in 4s (6313 kB/s)  
[...]

Service will be enabled by default.

$ systemctl status couchdb.service
* couchdb.service - Apache CouchDB
   Loaded: loaded (/lib/systemd/system/couchdb.service; enabled; vendor preset: enabled)
   Active: active (running) since Sun 2020-02-09 15:16:33 GMT; 1min 5s ago
 Main PID: 15648 (beam.smp)
    Tasks: 39 (limit: 242)
   Memory: 39.3M
   CGroup: /system.slice/couchdb.service
           |-15648 /opt/couchdb/bin/../erts-9.3.3.11/bin/beam.smp -K true -A 16 -Bd -- -root /opt/couchdb/bin/.. -progname couchdb -- -home /opt/couchdb -- -boot
           |-15658 /opt/couchdb/bin/../erts-9.3.3.11/bin/epmd -daemon
           |-15677 erl_child_setup 1024
           |-15693 sh -s disksup
           |-15695 /opt/couchdb/bin/../lib/os_mon-2.4.4/priv/bin/memsup
           `-15696 /opt/couchdb/bin/../lib/os_mon-2.4.4/priv/bin/cpu_sup

Stop it immediately.

$ sudo systemctl stop couchdb.service

Delete current databases and stored data.

$ sudo find /opt/couchdb/data/ -maxdepth 1 -delete

Configure Erlang VM

Inspect default Erlang VM settings.

$ sudo cat /opt/couchdb/etc/vm.args 
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.

# Each node in the system must have a unique name. These are specified through
# the Erlang -name flag, which takes the form [email protected] CouchDB
# recommends the following values for this flag:
#
# 1. If this is a single node, not in a cluster, use:
#    -name [email protected]
# 2. If DNS is configured for this host, use the FQDN, such as:
#    -name [email protected]
# 3. If DNS isn't configured for this host, use IP addresses only, such as:
#    -name [email protected]
#
# Do not rely on tricks with /etc/hosts or libresolv to handle anything
# other than the above 3 approaches correctly.
#
# Multiple CouchDBs running on the same machine can use [email protected], [email protected],
# etc.
-name [email protected]

# All nodes must share the same magic cookie for distributed Erlang to work.
# Comment out this line if you synchronized the cookies by other means (using
# the ~/.erlang.cookie file, for example).
-setcookie monster

# Tell kernel and SASL not to log anything
-kernel error_logger silent
-sasl sasl_error_logger false

# Use kernel poll functionality if supported by emulator
+K true

# Start a pool of asynchronous IO threads
+A 16

# Comment this line out to enable the interactive Erlang shell on startup
+Bd -noinput

# Force use of the smp scheduler, fixes #1296
-smp enable

# Set maximum SSL session lifetime to reap terminated replication readers
-ssl session_lifetime 300

Change the node name. This is a single-node setup, so it does not need to be reachable address, thus I will use fox prefix and 127.0.01 IP address.

$ sudo sed -i -e "s/^-name .*/-name [email protected]/" /opt/couchdb/etc/vm.args

Change default Erlang cookie. It needs to be the same on every cluster node, when you decide to expand later.

$ sudo sed -i -e "s/^-setcookie .*/-setcookie $(openssl rand -base64 24)/" /opt/couchdb/etc/vm.args

Configure CouchDB

Inspect default CouchDB settings.

$ sudo cat /opt/couchdb/etc/local.ini
; CouchDB Configuration Settings

; Custom settings should be made in this file. They will override settings
; in default.ini, but unlike changes made to default.ini, this file won't be
; overwritten on server upgrade.

[couchdb]
;max_document_size = 4294967296 ; bytes
;os_process_timeout = 5000
uuid = 9a716e184ccc59649adc13279b4eeb8d

[couch_peruser]
; If enabled, couch_peruser ensures that a private per-user database
; exists for each document in _users. These databases are writable only
; by the corresponding user. Databases are in the following form:
; userdb-{hex encoded username}
;enable = true
; If set to true and a user is deleted, the respective database gets
; deleted as well.
;delete_dbs = true
; Set a default q value for peruser-created databases that is different from
; cluster / q
;q = 1

[chttpd]
;port = 5984
;bind_address = 127.0.0.1
; Options for the MochiWeb HTTP server.
;server_options = [{backlog, 128}, {acceptor_pool_size, 16}]
; For more socket options, consult Erlang's module 'inet' man page.
;socket_options = [{sndbuf, 262144}, {nodelay, true}]

[httpd]
; NOTE that this only configures the "backend" node-local port, not thed
; "frontend" clustered port. You probably don't want to change anything in
; this section.
; Uncomment next line to trigger basic-auth popup on unauthorized requests.
;WWW-Authenticate = Basic realm="administrator"

; Uncomment next line to set the configuration modification whitelist. Only
; whitelisted values may be changed via the /_config URLs. To allow the admin
; to change this value over HTTP, remember to include {httpd,config_whitelist}
; itself. Excluding it from the list would require editing this file to update
; the whitelist.
;config_whitelist = [{httpd,config_whitelist}, {log,level}, {etc,etc}]

[query_servers]
;nodejs = /usr/local/bin/couchjs-node /path/to/couchdb/share/server/main.js

[couch_httpd_auth]
; If you set this to true, you should also uncomment the WWW-Authenticate line
; above. If you don't configure a WWW-Authenticate header, CouchDB will send
; Basic realm="server" in order to prevent you getting logged out.
; require_valid_user = false

[ssl]
;enable = true
;cert_file = /full/path/to/server_cert.pem
;key_file = /full/path/to/server_key.pem
;password = somepassword
; set to true to validate peer certificates
;verify_ssl_certificates = false
; Set to true to fail if the client does not send a certificate. Only used if verify_ssl_certificates is true.
;fail_if_no_peer_cert = false
; Path to file containing PEM encoded CA certificates (trusted
; certificates used for verifying a peer certificate). May be omitted if
; you do not want to verify the peer.
;cacert_file = /full/path/to/cacertf
; The verification fun (optional) if not specified, the default
; verification fun will be used.
;verify_fun = {Module, VerifyFun}
; maximum peer certificate depth
;ssl_certificate_max_depth = 1
;
; Reject renegotiations that do not live up to RFC 5746.
;secure_renegotiate = true
; The cipher suites that should be supported.
; Can be specified in erlang format "{ecdhe_ecdsa,aes_128_cbc,sha256}"
; or in OpenSSL format "ECDHE-ECDSA-AES128-SHA256".
;ciphers = ["ECDHE-ECDSA-AES128-SHA256", "ECDHE-ECDSA-AES128-SHA"]
; The SSL/TLS versions to support
;tls_versions = [tlsv1, 'tlsv1.1', 'tlsv1.2']

; To enable Virtual Hosts in CouchDB, add a vhost = path directive. All requests to
; the Virtual Host will be redirected to the path. In the example below all requests
; to http://example.com/ are redirected to /database.
; If you run CouchDB on a specific port, include the port number in the vhost:
; example.com:5984 = /database
[vhosts]
;example.com = /database/

; To create an admin account uncomment the '[admins]' section below and add a
; line in the format 'username = password'. When you next start CouchDB, it
; will change the password to a hash (so that your passwords don't linger
; around in plain-text files). You can add more admin accounts with more
; 'username = password' lines. Don't forget to restart CouchDB after
; changing this.
[admins]
;admin = mysecretpassword

Ensure that CouchDB HTTP Server will listen on any available IP address, alternatively specify particular IP address.

$ cat << EOF | tee /opt/couchdb/etc/local.d/10-chttpd-bind.ini
[chttpd]
port = 5984
bind_address = 0.0.0.0
EOF
$ chown couchdb:couchdb /opt/couchdb/etc/local.d/10-chttpd-bind.ini

Generate custom UUID. It needs to be the same on every cluster node, when you decide to expand later.

$ openssl rand -hex 15
24e9eaa4b231ed297c68a9f9f4bfb1
$ cat << EOF | tee /opt/couchdb/etc/local.d/10-couchdb-uuid.ini
[couchdb]
uuid = 24e9eaa4b231ed297c68a9f9f4bfb1
EOF
$ chown couchdb:couchdb /opt/couchdb/etc/local.d/10-couchdb-uuid.ini

Ensure that everyone needs to be authenticated on port 5984 (clustered-port).

$ cat << EOF | tee /opt/couchdb/etc/local.d/11-chttpd-auth-5984.ini
[chttpd]
require_valid_user = true
EOF
$ chown couchdb:couchdb /opt/couchdb/etc/local.d/11-chttpd-auth-5984.ini

Ensure that everyone needs to be authenticated on port 5986 (node-local port).

$ cat << EOF | tee /opt/couchdb/etc/local.d/11-chttpd-auth-5986.ini
[couch_httpd_auth]
require_valid_user = true
EOF
$ chown couchdb:couchdb /opt/couchdb/etc/local.d/11-chttpd-auth-5986.ini

Define default security when not explicitly set.

$ cat << EOF | tee /opt/couchdb/etc/local.d/10-couchdb-default-security.ini
[couchdb]
default_security = admin_only
EOF
$ chown couchdb:couchdb /opt/couchdb/etc/local.d/10-couchdb-default-security.ini

Generate admin password.

$ PASS="admin_password" SALT="5ffa3ff6471d4cbda5e444e5e34b1c51" ITER=10 \
  python3 -c "import os,hashlib; print('-pbkdf2-%s,%s,%s' % (hashlib.pbkdf2_hmac('sha1',os.environ['PASS'].encode(),os.environ['SALT'].encode(),int(os.environ['ITER'].encode())).hex(), os.environ['SALT'], os.environ['ITER']))"
-pbkdf2-f9ab866e5960d865b30f999932415a398b116d33,5ffa3ff6471d4cbda5e444e5e34b1c51,10

Define admin administrator wit admin_password password.

$ cat << EOF | tee /opt/couchdb/etc/local.d/12-admins.ini
[admins]
admin = -pbkdf2-f9ab866e5960d865b30f999932415a398b116d33,5ffa3ff6471d4cbda5e444e5e34b1c51,10
EOF
$ chown couchdb:couchdb /opt/couchdb/etc/local.d/12-admins.ini

Start service afterwards.

$ sudo systemctl stop couchdb.service

Verify service state

Confirm that authentication is required.

$ curl http://192.168.50.201:5984/
{"error":"unauthorized","reason":"Authentication required."}

Display known nodes.

$ curl --silent --user admin:admin_password http://192.168.50.201:5984/_membership
{"all_nodes":["[email protected]"],"cluster_nodes":["[email protected]"]}

Create initial databases

These databases are crucial for the proper functioning of CouchDB.

Create initial databases.

$ curl -X PUT --silent --user admin:admin_password http://192.168.50.201:5984/_users
{"ok":true}
$ curl -X PUT --silent --user admin:admin_password http://192.168.50.201:5984/_replicator
{"ok":true}

Display created databases.

$ curl --silent --user admin:admin_password http://192.168.50.201:5984/_all_dbs
["_replicator","_users"]

Use web-interface to manage CouchDB

Use http://192.168.50.201:5984/_utils/ address to manage CouchDB with Fauxton - a native web-based interface built into CouchDB.

CouchDB
CouchDB native web-based interface

Play with it

Create notes database.

$ curl -X PUT --silent --user admin:admin_password http://192.168.50.201:5984/notes
{"ok":true}

Create milosz user with milosz_password password.

$ curl -X PUT --silent --user admin:admin_password http://192.168.50.201:5984/_users/org.couchdb.user:milosz -d '{"name": "milosz", "password": "milosz_password", "roles": [], "type": "user"}' -H "Accept: application/json" -H "Content-Type: application/json" 
{"ok":true,"id":"org.couchdb.user:milosz","rev":"1-d760cc4e496e9815d8b0d9031bf8eda5"}

Add milosz user as member to notes database.

$ curl -X PUT --silent --user admin:admin_password http://192.168.50.201:5984/notes/_security -X PUT -d '{ "members": { "names": [ "milosz" ] } }'
{"ok":true}

Get random UUID.

$ curl --silent --user milosz:milosz_password "http://192.168.50.201:5984/_uuids?count=1"
{"uuids":["bd8c3ea87b89463b7c10f1a5e800b2fd"]}

Create single document.

$ curl --silent --user milosz:milosz_password "http://192.168.50.201:5984/notes/bd8c3ea87b89463b7c10f1a5e800b2fd" -X PUT -d '{ "note": "Does it work?" }'
{"ok":true,"id":"bd8c3ea87b89463b7c10f1a5e800b2fd","rev":"1-34c19acf58a28d5553531538529c7833"}

Get document.

$ curl --silent --user milosz:milosz_password "http://192.168.50.201:5984/notes/bd8c3ea87b89463b7c10f1a5e800b2fd"
{"_id":"bd8c3ea87b89463b7c10f1a5e800b2fd","_rev":"1-34c19acf58a28d5553531538529c7833","note":"Does it work?"}

Update document.

$ curl --silent --user milosz:milosz_password "http://192.168.50.201:5984/notes/bd8c3ea87b89463b7c10f1a5e800b2fd" -X PUT -d '{ "note": "Does it work? Yes.", "_rev":"1-34c19acf58a28d5553531538529c7833" }'
{"ok":true,"id":"bd8c3ea87b89463b7c10f1a5e800b2fd","rev":"2-8eafb58d9b474fef1ff41dcb00b98c50"}

List documents.

$ curl --silent --user milosz:milosz_password "http://192.168.50.201:5984/notes/_all_docs"
{"total_rows":1,"offset":0,"rows":[
{"id":"bd8c3ea87b89463b7c10f1a5e800b2fd","key":"bd8c3ea87b89463b7c10f1a5e800b2fd","value":{"rev":"2-8eafb58d9b474fef1ff41dcb00b98c50"}}
]}

Get updated document.

$ curl --silent --user milosz:milosz_password "http://192.168.50.201:5984/notes/bd8c3ea87b89463b7c10f1a5e800b2fd" 
{"_id":"bd8c3ea87b89463b7c10f1a5e800b2fd","_rev":"2-8eafb58d9b474fef1ff41dcb00b98c50","note":"Does it work? Yes."}

List document revisions.

$ curl --silent --user milosz:milosz_password "http://192.168.50.201:5984/notes/bd8c3ea87b89463b7c10f1a5e800b2fd?revs_info=true" | jq
{
  "_id": "bd8c3ea87b89463b7c10f1a5e800b2fd",
  "_rev": "2-8eafb58d9b474fef1ff41dcb00b98c50",
  "note": "Does it work? Yes.",
  "_revs_info": [
    {
      "rev": "2-8eafb58d9b474fef1ff41dcb00b98c50",
      "status": "available"
    },
    {
      "rev": "1-34c19acf58a28d5553531538529c7833",
      "status": "available"
    }
  ]
}

Get specific document revision.

$ curl --silent --user milosz:milosz_password "http://192.168.50.201:5984/notes/bd8c3ea87b89463b7c10f1a5e800b2fd?rev=1-34c19acf58a28d5553531538529c7833" | jq
{
  "_id": "bd8c3ea87b89463b7c10f1a5e800b2fd",
  "_rev": "1-34c19acf58a28d5553531538529c7833",
  "note": "Does it work?"
}

Additional notes

Erlang VM flags

Apache CouchDB® Documentation

Fauxton - a native web-based interface built into CouchDB