Apache CouchDB speaks plain HTTP and JSON on port 5984, so every setting that decides who can reach it, who can log in and what each identity can touch is a customer-side choice that needs to be made deliberately. CouchDB 3.x gives operators a solid set of controls: a mandatory server admin at startup, admin-only new databases, per-database _security objects, cookie, proxy and JWT authentication, built-in TLS and a shared Erlang cookie that protects cluster traffic. This guide shows how to use them together: network binding and ports, server admins, users and roles, database authorisation, replication and service credentials, logging and a hardening baseline you can roll out in phases. It is written for platform engineers, DBAs and security architects who run self-managed CouchDB, and it goes deeper than the general Database Security Guide.
This is an independent guide from NHI Mgmt Group, based on Apache CouchDB's public documentation as of November 2026. It is not sponsored or reviewed by the Apache CouchDB project. Product features change, so confirm settings against Apache CouchDB's current documentation.
Key takeaways
- Admin Party is over in 3.x. Since CouchDB 3.0 a server admin must be defined in the
[admins]section before the node will start, and new databases are admin-only. Keep those defaults and build named users and roles on top of them. - Treat the Erlang cookie as a cluster root secret. The
-setcookievalue inetc/vm.argsis the password nodes use to join each other. Generate a long random value, store it like a credential and keep ports 4369 and 9100-9200 on a private network. - Bind narrowly and put TLS in front. Set
[chttpd] bind_addressto a private or loopback interface, enable the[ssl]section withtls_versionslimited to TLS 1.2 or later, or terminate TLS at a reverse proxy. - Use
_securityobjects for every database. Assignmembersandadminsby role, avoiddefault_security = everyone, and turn onrequire_valid_userso anonymous requests are refused. - Replication documents hold credentials. Use the
authobject with dedicated, least-privilege replication users, rotate them, and restrict who can read the_replicatordatabase.
How identity and access work in Apache CouchDB
CouchDB has two layers of identity. Server admins live in the node configuration ([admins] in local.ini), are stored as salted PBKDF2 hashes (prefixed -pbkdf2-) and hold every privilege on the node. Users live as documents in the _users system database, with IDs of the form org.couchdb.user:<name>, a password that CouchDB hashes on write and a roles array that only admins can change. A user can never be given the _admin role through _users.
Authorisation for data is set per database through the _security object, which has two lists, each with names and roles:
| Level | What it allows | How it is set |
|---|---|---|
| Server admin | Everything: create and delete databases, edit _security, design documents, _config, _compact, _purge, _active_tasks, node restart | [admins] in local.ini or PUT /_node/_local/_config/admins/<name> |
| Database admin | Read and write all documents including design documents, change membership and per-database settings | admins list in PUT /{db}/_security |
| Database member | Read all documents and write non-design documents | members list in PUT /{db}/_security |
_metrics role | Read only /_node/{node}/_stats and /_node/{node}/_system | Role in the user document (added in 3.0) |
| Anonymous | Only what a database's _security allows, plus /_up and /_session | Refuse entirely with [chttpd] require_valid_user = true |
Members can write documents but cannot change design documents, so finer rules (for example, a user may only edit their own documents) belong in a validate_doc_update function inside a design document, which only database admins can deploy.
Authentication methods
The [chttpd] authentication_handlers setting lists the methods the node accepts: cookie, default (HTTP Basic), proxy and jwt. Choose deliberately:
| Method | Use it for | Key settings |
|---|---|---|
| Basic | Service-to-service calls over TLS | Always pair with TLS, since credentials travel with every request; since 3.4 a successful Basic login also returns a session cookie |
Cookie (POST /_session) | Browser and interactive sessions | [chttpd_auth] timeout (sessions last 10 minutes by default), secret, same_site (lax or strict), allow_persistent_cookies |
| TOTP two-factor | Human users who log in through /_session | A per-user totp.key in the user document |
| Proxy | An SSO gateway that has already authenticated the user | Headers X-Auth-CouchDB-UserName, X-Auth-CouchDB-Roles, X-Auth-CouchDB-Token; set proxy_use_secret = true so the token is an HMAC with the shared secret |
| JWT | Tokens from your identity provider instead of _users accounts | [jwt_keys] entries as <alg>:<kid> (HMAC, RSA or EC), [jwt_auth] required_claims such as exp,iat, roles_claim_path |
Decision guidance: small teams with one application usually do well with Basic over TLS for the service plus cookie sessions for a few operators. Larger organisations should move human access to JWT or proxy authentication backed by the corporate identity provider, so joiners and leavers are handled centrally and MFA is enforced upstream. With JWT, CouchDB takes the username from the mandatory sub claim and rejects the none algorithm; every cluster node must carry the same key list. See the Database Credential and Access Security Guide for the wider pattern.
Admin roles and privileged access
Server admin is the most powerful identity in CouchDB, so keep the set small and named.
- Bootstrap one admin, then name the rest: CouchDB needs one admin in
[admins]to start. Use a long random password from your secrets manager at install time, then create named admin accounts for each operator through the config API and retire the generic bootstrap name. - Keep admin passwords out of plain-text files: CouchDB replaces a plain-text
[admins]entry with its hash on startup. Make sure configuration management templates, container images and CI logs never keep the plain-text form after first boot. - Prefer database admins to server admins: most application owners need to deploy design documents and manage membership for their own databases, which the
adminslist in_securitygrants without node-wide power. - Use
_metricsfor monitoring: give scrapers and dashboards a user with only the_metricsrole rather than admin credentials. - Tune password hashing: set
[chttpd_auth] iterationsandmin_iterationsto values your hardware can sustain, keephash_algorithmson SHA-2 family values (the docs advise againstmd4andmd5), and leaveupgrade_hash_on_authon (the default since 3.5.0) so older hashes are upgraded at the next login. - Protect the
_usersdatabase: keepusers_db_public = false(the default) so only admins can browse user documents. If you need a public profile field, list it inpublic_fieldsrather than opening the whole document.
For broader controls over admin sessions, approvals and recording, see the Privileged Access Management Guide.
Integrations, API tokens and service accounts
Most CouchDB traffic comes from applications, sync clients and replication jobs, which makes these non-human identities the largest credential population on the node.
- One user per application: create a
_usersaccount per service with a role named after it, and add that role to thememberslist of only the databases it needs. Never let applications run as a server admin. - Replication credentials: documents in
_replicatorcarry source and target credentials. Use theauthobject ("auth": {"basic": {"username": ..., "password": ...}}, available since 3.2.0) rather thanuser:pass@in the URL, which the docs describe as deprecated under RFC 3986, or a base64Authorizationheader, which the docs note can look encrypted when it is not. Give replication users member rights on the specific source and target databases only. - Control who runs replications: for non-admin users the replicator validation function requires
user_ctxto carry their own name; only admins can run a replication on behalf of another user. Restrict write access to_replicatoraccordingly. - JWT for workloads: where your platform issues workload tokens, the
jwthandler lets services authenticate with short-lived tokens and no stored password. Requireexpinrequired_claimsso tokens expire. - Rotation: change a user's password by updating its
_usersdocument with a newpasswordfield and the current_rev; rotate the cookiesecret, proxy secret and JWT keys on a schedule and on staff changes. Store all of them in a secrets manager, not in application repositories. See the Secrets Management Guide.
Logging and detection
CouchDB writes its log through the [log] section. Set writer to file, syslog or journald and ship the output to your SIEM; for syslog the docs state that syslog_host is mandatory, alongside syslog_port and syslog_facility. Run at level = info or notice in production so requests and authentication outcomes are recorded without debug noise, and never set none.
| Signal | Where to see it | Why it matters |
|---|---|---|
Repeated 401 responses on /_session or any path | CouchDB request log | Password guessing against users or admins |
PUT /_node/*/_config/*, especially admins | Request log | New server admins or changed security settings |
PUT /{db}/_security | Request log | Membership changes that widen access |
| New or changed design documents | Request log, _changes feed | Validation logic or views altered |
New _replicator documents to unknown hosts | _replicator changes, _scheduler/jobs | Data copied to an unexpected target |
| Connections to 4369 or 9100-9200 from non-cluster hosts | Firewall or flow logs | Attempts to reach Erlang distribution |
Watch the HTTP interface from outside as well: an external scan that finds 5984, 6984, 4369 or the distribution range reachable from the internet is an incident to fix the same day. In MITRE ATT&CK terms these detections cover Exploit Public-Facing Application (T1190), Valid Accounts (T1078) and Unsecured Credentials (T1552) for replication passwords left in readable places. See the Database Audit Log Detection Guide.
Metrics that show the controls work:
- Number of CouchDB endpoints reachable from the internet (target: zero).
- Percentage of databases whose
_securityobject names explicit roles (target: 100%). - Number of server admins, and the share that are named individuals rather than shared accounts.
- Age of the oldest replication and service credential against your rotation policy.
- Percentage of nodes on a supported release with the latest security update applied.
Hardening baseline
Network binding and ports
| Port | Purpose | Recommended exposure |
|---|---|---|
| 5984/tcp | HTTP API ([chttpd] port) | Loopback for a single node behind a local proxy; private interface for clusters; reach it from users only through TLS |
| 6984/tcp | HTTPS, as used in the docs' TLS example | The application network, never the open internet without an authenticating gateway |
| 4369/tcp | Erlang port mapper (epmd) | Loopback on a single node; private cluster interface only when clustered |
| 9100-9200/tcp | Erlang inter-node distribution, pinned with inet_dist_listen_min and inet_dist_listen_max in vm.args | Private cluster interface, allowed only between cluster members |
The cluster documentation is explicit that if the distribution port is reachable from untrusted networks, the Erlang cookie is the only protection. A peer holding the cookie can control the node completely, so treat cookie and network as a pair: a strong random -setcookie value shared only by cluster nodes, and firewall rules that limit 4369 and 9100-9200 to those nodes. Set -name couchdb@<fqdn> so node identity follows DNS, and rotate the cookie across the whole cluster in a planned window if it may have been exposed.
Baseline settings
- Authentication required:
[chttpd] require_valid_user = true(defaultfalse). Userequire_valid_user_except_for_up = trueif a load balancer needs anonymous health checks on/_up. - Database defaults: keep new databases admin-only; do not set
[couchdb] default_security = everyone; keepadmin_only_all_dbs = true(the default since 3.0) so database names are not listed to ordinary users. - TLS: in
[ssl]setenable = true,cert_file,key_file,cacert_file,tls_versions = ['tlsv1.2']or newer, and a reviewedcipherslist; setverify_ssl_certificatesandfail_if_no_peer_certtotruewhere you want client certificates. If a reverse proxy terminates TLS, bind CouchDB to loopback and configurex_forwarded_protohandling. See the Database and Storage Encryption Guide. - Cookies: set
[chttpd_auth] same_site = strictorlax, a shorttimeoutfor admin sessions and a uniquesecretshared by all nodes. - CORS: leave
enable_cors = falseunless a browser app needs it; then list exactorigins(the docs note*cannot be combined withcredentials = true). - Information and limits: set
server_header_versions = falseto drop version details from theServerheader, setmax_http_request_sizeto what your largest document needs, and enableenable_xframe_optionsfor Fauxton. - Updates: run a supported release and apply security updates promptly; the project publishes fixes for the current and previous release series and lists advisories on its security issues page.
Phased rollout
- Inventory: list every CouchDB node, its version,
bind_address, open ports and the_securityobject of each database (GET /{db}/_securityas admin). - Network first: restrict 4369 and 9100-9200 to cluster peers and remove any internet route to 5984 or 6984. This is the change with the largest risk reduction and the least application impact.
- Identities: create per-application users and roles, add them to
_security, move replication documents to theauthobject and replace shared admin use. - Pilot
require_valid_user: enable it on a staging node, run the application test suite and confirm health checks still pass, then enable it node by node in production. - TLS and cookie hardening: enable
[ssl]or the proxy, set cookie attributes, then rotate the Erlang cookie and cookiesecret. - Roll back safely: every setting above can be reverted through
_configorlocal.ini; keep the prior values in version control and change one setting at a time so a failing client points to a single cause.
Framework mapping
| Control area | NIST SP 800-53 Rev 5 | NIST CSF 2.0 | CIS Controls v8.1 | ISO/IEC 27001:2022 |
|---|---|---|---|---|
| Bind, firewall, Erlang ports | SC-7, CM-7 | PR.PS-01 | 4 (Secure Configuration), 12 (Network Infrastructure) | A.8.20 Networks security |
Admins, users, _security roles | AC-6, IA-2 | PR.AA-05 | 5 (Account Management), 6 (Access Control Management) | A.5.15 Access control, A.8.2 Privileged access rights |
| Passwords, cookie, Erlang cookie, JWT keys | IA-5 | PR.AA-05 | 5, 6 | A.5.17 Authentication information |
| TLS | SC-8 | PR.DS-02 | 3 (Data Protection) | A.8.24 Use of cryptography |
| Logging and monitoring | AU-2 | DE.CM-01 | 8 (Audit Log Management) | A.8.15 Logging, A.8.16 Monitoring activities |
Common misconfigurations
- Binding to
0.0.0.0on a public host: the cluster setup example uses0.0.0.0for reachability inside a private network. Copying it to an internet-facing host without a firewall publishes the API. Bind to the private interface and filter at the network edge. - Reusing the example Erlang cookie: sample values from documentation or container images end up in production. Generate a unique random cookie per cluster and keep the distribution ports private.
- Opening databases with
default_security = everyone: this treats anonymous and ordinary users as members of every new database. Leave the admin-only default and grant roles explicitly. - Applications running as server admin: a leaked application password then grants
_configaccess. Give each application a member role on its own databases. - Credentials in replication URLs:
user:pass@hoststrings are copied into tickets and scripts. Use theauthobject with a dedicated replication user and rotate it. - Basic authentication without TLS: credentials cross the network in every request. Enable
[ssl]or terminate TLS at a proxy before allowing remote clients.
Practitioner checklist
- Confirm every node runs CouchDB 3.x or later on a supported release with current security updates.
- Bind 5984 to loopback or a private interface and confirm no internet route to 5984 or 6984.
- Restrict 4369 and 9100-9200 to cluster peers and replace any example Erlang cookie with a unique random value.
- Replace the bootstrap admin with named server admins and keep the count small.
- Set
require_valid_user = trueand keep new databases admin-only. - Give every database a
_securityobject with role-basedmembersandadmins. - Create one user per application and per replication job, with no server admin rights.
- Move replication credentials into the
authobject and store all secrets in a secrets manager. - Enable TLS 1.2 or later and set
same_siteandtimeoutfor session cookies. - Ship
[log]output to the SIEM and alert on_config,_securityand replication changes.
Standards and references
- Apache CouchDB: Security
- Apache CouchDB: Authentication and Authorization configuration
- Apache CouchDB: CouchDB HTTP Server configuration
- Apache CouchDB: Cluster Set Up
- Apache CouchDB: Authentication API
- Apache CouchDB: Replicator Database
- Apache CouchDB: 3.0.x Release Notes
- NIST: SP 800-53 Rev. 5, Security and Privacy Controls
Related NHI Mgmt Group resources: Database Security Guide · Database Credential and Access Security Guide · Database Audit Log Detection Guide · Secrets Management Guide