Deployment guide

FerretDB 2.7.0

Overview

FerretDB is an open-source database that speaks the MongoDB wire protocol. Applications, MongoDB drivers, mongosh and tools such as MongoDB Compass connect to it as they would to MongoDB, and FerretDB stores the documents in PostgreSQL through the DocumentDB extension.

This guide covers launching and using FerretDB 2.7.0 on Ubuntu 24.04 from SecureAnyCloud on Amazon EC2. The image runs FerretDB 2.7.0 on PostgreSQL 17 with DocumentDB 0.107.0 (the release that FerretDB 2.7.0 is built for), on one instance. It is one version of the SecureAnyCloud Hardened Apache Cassandra, FerretDB & NoSQL Database AMIs listing on AWS Marketplace, and you choose it as the software version when you subscribe and launch. The listing's guide hub describes the other databases in the listing.

What you get:

  • No default login, and a password unique to your instance. The image ships no PostgreSQL cluster and no database. First boot creates this instance's own cluster on the data volume, installs DocumentDB and creates the MongoDB administrator user admin with a 32-character password generated on the instance. Only a SCRAM-SHA-256 verifier of that password is handed to PostgreSQL. The password itself never reaches PostgreSQL, a command line, a log or the journal. It is written to the root-only file /root/ferretdb-credentials.txt. FerretDB and PostgreSQL refuse to start until first boot has finished.
  • PostgreSQL with no network port and no password. PostgreSQL has no TCP listener at all. FerretDB, DocumentDB's background jobs and you (as the operating system's postgres user) reach it over the local socket with peer authentication. Its postgres and ferretdb roles have no password, so there is no PostgreSQL credential to steal, and there is no trust rule anywhere in pg_hba.conf.
  • FerretDB on localhost, with TLS. FerretDB listens on 127.0.0.1:27017 and, with a certificate generated on your instance's first boot, on 127.0.0.1:27018 over TLS. Nothing but SSH is reachable from the network until you choose to open the TLS port to your VPC (Step 8).
  • A FerretDB build made for this image. The FerretDB 2.7.0 release binary was built with Go 1.25.4 and modules that now carry fixed security advisories. SecureAnyCloud therefore compiles FerretDB 2.7.0 from its release source with Go 1.27.3 and the fixed module releases, and govulncheck reports no vulnerabilities in the result. The build is reproducible, and its provenance is on the instance (/usr/share/doc/ferretdb/BUILD-PROVENANCE.txt).
  • Hardened services. FerretDB runs as an unprivileged ferretdb user with no capabilities, a read-only view of the system and no access to /root. FerretDB, PostgreSQL and the first-boot service are blocked from the instance metadata service, and the AMI requires IMDSv2. FerretDB's telemetry is off, and mongosh is set so that it sends no telemetry and keeps no session logs.
  • Your data on its own disk. The PostgreSQL data directory (/var/lib/ferretdb/pg17) is on a separate 20 GiB EBS volume that you can grow or snapshot on its own.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: FerretDB 2.7.0 (/usr/local/bin/ferretdb), PostgreSQL 17 with pg_cron, PostGIS, RUM and pgvector from the PostgreSQL Global Development Group (PGDG) repository, the DocumentDB extension 0.107.0 for FerretDB 2.7.0, mongosh 2.13.0 (the MongoDB Shell; only the open source shell, not MongoDB's proprietary encryption library), Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. There is no MongoDB server on the image.

Default user: ubuntu | Architecture: x86_64 | Recommended instance type: m5.large

Prerequisites

  • An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route that you use for management).
  • A security group that allows TCP 22 (SSH) from your admin network. FerretDB listens on the loopback address only, so no database port needs to be open. You reach FerretDB through an SSH tunnel (Step 7), or you can open the TLS port to your application subnet later (Step 8).
1

Launch the instance

In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Select FerretDB 2.7.0 on Ubuntu 24.04 as the software version, select your Region, and choose Launch through EC2. Pick m5.large, your key pair and a security group as described above.

The AMI includes two volumes: the root volume and a 20 GiB data volume (/dev/sdf) for /var/lib/ferretdb. Size the data volume for your data at launch.

To launch with the AWS CLI instead, replace the values in angle brackets. This example grows the data volume to 100 GiB:

aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
  --instance-type m5.large --key-name <your-key-pair> \
  --security-group-ids <your-security-group> --subnet-id <your-subnet> \
  --block-device-mappings 'DeviceName=/dev/sdf,Ebs={VolumeSize=100,VolumeType=gp3}' \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=ferretdb}]'

If you make the data volume larger at launch, grow the file system once after first boot with sudo resize2fs "$(findmnt -no SOURCE /var/lib/ferretdb)". First boot takes well under a minute after the instance is running.

2

Connect to your instance

Connect to FerretDB 2.7.0 on Ubuntu 24.04 as the SSH user ubuntu, with the key pair you chose at launch:

ssh -i <your-key.pem> ubuntu@<public-ip>
3

Check the services

First boot creates the PostgreSQL cluster, the administrator and the certificate, and then PostgreSQL and FerretDB start:

systemctl is-active ferretdb-firstboot postgresql@17-main ferretdb
sudo test -f /var/lib/sac/data/ferretdb-firstboot.done && echo FIRSTBOOT_DONE

After first boot, all three units are active and the first-boot marker is present. On later boots, ferretdb-firstboot shows inactive because it runs once:

active active active FIRSTBOOT_DONE

Only SSH listens on a network address. FerretDB listens on the loopback address on 27017 (MongoDB protocol), 27018 (MongoDB protocol over TLS) and 8088 (its health and metrics handler). PostgreSQL has no TCP port at all, only its local socket:

sudo ss -lntp | awk 'NR>1 {print $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | grep -v -E '^127\.0\.0\.5[34]' | sort
sudo ss -lxp | grep PGSQL | awk '{print $5}'
0.0.0.0:22 users:(("sshd"),("systemd")) 127.0.0.1:27017 users:(("ferretdb")) 127.0.0.1:27018 users:(("ferretdb")) 127.0.0.1:8088 users:(("ferretdb")) [::]:22 users:(("sshd"),("systemd")) /var/run/postgresql/.s.PGSQL.5432

The data is on the separate data volume:

findmnt -no SOURCE,FSTYPE /var/lib/ferretdb
sudo du -sh /var/lib/ferretdb/pg17
/dev/nvme1n1 ext4 75M /var/lib/ferretdb/pg17

FerretDB reports ready once PostgreSQL and DocumentDB answer:

curl -s -o /dev/null -w 'readyz HTTP %{http_code}\n' http://127.0.0.1:8088/debug/readyz
readyz HTTP 200
4

Get your administrator password

sudo cat /root/ferretdb-credentials.txt

Only root can read this file:

# FerretDB 2.7.0 on Ubuntu 24.04 - generated on first boot by ferretdb-firstboot.service
# This password is unique to this instance; none was baked into the image. Keep this file secret. FERRETDB_USERNAME=admin FERRETDB_PASSWORD=<FERRETDB_PASSWORD> FERRETDB_HOST=127.0.0.1 FERRETDB_PORT=27017 FERRETDB_TLS_PORT=27018 FERRETDB_TLS_CA=/etc/ferretdb/tls/ferretdb.crt
# On the instance (mongosh signed in as admin; the password never goes on a command line):
sudo ferretdb-shell
# Connection string, on the instance or through an SSH tunnel to 127.0.0.1:27017 (replace <password>):
# mongodb://admin:<password>@127.0.0.1:27017/?authMechanism=SCRAM-SHA-256
# TLS listener (127.0.0.1:27018 by default; give clients /etc/ferretdb/tls/ferretdb.crt as the CA file):
# mongodb://admin:<password>@<host>:27018/?authMechanism=SCRAM-SHA-256&tls=true&tlsCAFile=ferretdb.crt
# FerretDB listens on the loopback interface only; port 27017 and 27018 are not open to the network.
# User guide: https://secureanycloud.com/stacks/ferretdb-2-7-0/
5

Use the MongoDB shell on the instance

sudo ferretdb-shell opens mongosh signed in as admin. It reads the password from the credentials file itself, so the password never appears on a command line or in your shell history. With no arguments it opens an interactive shell, and with --eval it runs a command and exits:

sudo ferretdb-shell --eval 'db.runCommand({ping: 1})'
{ ok: 1 }

Write some documents, add an index and query the documents, as you would on MongoDB:

sudo ferretdb-shell --eval 'const inv = db.getSiblingDB("inventory"); inv.items.insertMany([{sku: "A-100", qty: 25}, {sku: "B-200", qty: 40}, {sku: "C-300", qty: 75}]); inv.items.createIndex({sku: 1}, {unique: true}); printjson(inv.items.find({qty: {$gt: 30}}, {_id: 0}).sort({qty: 1}).toArray())'
[ { sku: 'B-200', qty: 40 }, { sku: 'C-300', qty: 75 } ]

FerretDB identifies itself in buildInfo:

sudo ferretdb-shell --eval 'printjson(db.adminCommand({buildInfo: 1}).ferretdb)'
{ version: 'v2.7.0', package: 'sac' }

Remove the example database when you have finished:

sudo ferretdb-shell --eval 'db.getSiblingDB("inventory").dropDatabase()'
{ ok: 1, dropped: 'inventory' }

FerretDB covers the MongoDB features most applications use (CRUD, indexes including text indexes, aggregation pipelines, cursors and user management). Before you migrate, check the FerretDB compatibility documentation for the commands and operators that your application relies on.

6

Create a user for your application

Give each application its own user, so that you can change or revoke its password without touching admin. Run the following in sudo ferretdb-shell. The passwordPrompt() function asks for the new password, so the password is never written on a command line:

use admin db.createUser({user: "appuser", pwd: passwordPrompt(), roles: [{role: "clusterAdmin", db: "admin"}, {role: "readWriteAnyDatabase", db: "admin"}]})

FerretDB 2.7.0 stores every user as a PostgreSQL role and accepts two role sets: clusterAdmin with readWriteAnyDatabase, and readAnyDatabase. FerretDB does not yet enforce MongoDB roles: every user it authenticates can read and write every database. Use separate users to separate credentials, and keep network access to FerretDB as narrow as you can (Steps 7 and 8). Change a user's password with db.getSiblingDB("admin").updateUser("appuser", {pwd: passwordPrompt()}), and remove one with db.getSiblingDB("admin").dropUser("appuser").

7

Connect from your own computer through an SSH tunnel

FerretDB is not exposed to the network. From your computer, open an SSH tunnel, and point mongosh, MongoDB Compass or your application at the local end of the tunnel. The tunnel encrypts the connection:

ssh -i <your-key.pem> -N -L 27017:127.0.0.1:27017 ubuntu@<public-ip> mongosh "mongodb://admin@127.0.0.1:27017/?authMechanism=SCRAM-SHA-256" -p

-p with no value makes mongosh prompt for the password. In MongoDB Compass use the connection string mongodb://admin:<password>@127.0.0.1:27017/?authMechanism=SCRAM-SHA-256&directConnection=true.

8

Allow TLS connections from your VPC

To let application servers in your VPC connect directly, use FerretDB's TLS listener rather than the plain one. The certificate generated on first boot names localhost, 127.0.0.1 and the instance's private and public EC2 names and addresses. Add your own DNS name if clients use one, open the TLS listener to the network, and restart FerretDB:

sudo ferretdb-tls-cert --renew db.example.internal
sudo sed -i 's/^FERRETDB_LISTEN_TLS=127.0.0.1:27018$/FERRETDB_LISTEN_TLS=0.0.0.0:27018/' /etc/ferretdb/ferretdb.env
sudo systemctl restart ferretdb

Then add an inbound rule for TCP 27018 from your application subnet (for example 10.0.1.0/24) to the instance's security group. Never open 27018 or 27017 to the internet. The plain listener on 27017 stays on the loopback address. Copy /etc/ferretdb/tls/ferretdb.crt to the clients and use it as their CA file:

mongodb://appuser:<password>@<private-dns-or-ip>:27018/?authMechanism=SCRAM-SHA-256&tls=true&tlsCAFile=/path/to/ferretdb.crt

To use a certificate from your own certificate authority instead, replace /etc/ferretdb/tls/ferretdb.crt and ferretdb.key (keep the key 0640, owner root, group ferretdb) and restart FerretDB.

9

Security checks

PostgreSQL accepts local socket connections with peer authentication only, and has no TCP listener:

sudo -u postgres psql -X -At -c "SELECT type, array_to_string(user_name, ','), auth_method, coalesce(array_to_string(options, ','), '') FROM pg_hba_file_rules ORDER BY line_number" pg_isready -h /var/run/postgresql -p 5432 pg_isready -h 127.0.0.1 -p 5432 || true
local|postgres|peer| local|all|peer|map=sac /var/run/postgresql:5432 - accepting connections 127.0.0.1:5432 - no response

Only admin, the MongoDB user, has a password, stored as a SCRAM-SHA-256 verifier. PostgreSQL's own roles have none:

sudo -u postgres psql -X -c "SELECT rolname, rolsuper, left(rolpassword, 13) AS password FROM pg_authid WHERE rolcanlogin ORDER BY 1"
rolname | rolsuper | password ---------------------------+----------+--------------- admin | f | SCRAM-SHA-256 documentdb_bg_worker_role | f | ferretdb | t | postgres | t | (4 rows)

The image's own self-test signs in as admin on both listeners (verifying this instance's certificate on the TLS one), and it writes, reads, indexes and aggregates data. It also proves that wrong and empty passwords, anonymous reads, the password-less PostgreSQL roles, TLS without the instance's certificate and plain text on the TLS port are all refused:

sudo /usr/local/sbin/ferretdb-roundtrip.sh
OK-ferretdb-scram-tls-roundtrip

The certificate was generated on this instance and names its own addresses. Only root and the ferretdb group can read the key:

openssl x509 -in /etc/ferretdb/tls/ferretdb.crt -noout -subject -ext subjectAltName ls -l /etc/ferretdb/tls | awk 'NR>1{print $1, $3, $4, $NF}'
subject=CN = ip-<private-ip-dashed>.ec2.internal, O = sac FerretDB X509v3 Subject Alternative Name: DNS:ip-<private-ip-dashed>.ec2.internal, IP Address:<private-ip>, DNS:ec2-<public-ip-dashed>.compute-1.amazonaws.com, IP Address:<public-ip>, DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1 -rw-r--r-- root root ferretdb.crt -rw-r----- root ferretdb ferretdb.key

FerretDB, PostgreSQL and the first-boot service cannot reach the instance metadata service, and FerretDB runs sandboxed as an unprivileged user:

systemctl show ferretdb postgresql@17-main ferretdb-firstboot -p Id -p IPAddressDeny
systemctl show ferretdb -p User -p ProtectSystem -p ProtectHome -p NoNewPrivileges -p ReadWritePaths -p CapabilityBoundingSet
IPAddressDeny=168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 Id=ferretdb.service IPAddressDeny=168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 Id=postgresql@17-main.service IPAddressDeny=168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 Id=ferretdb-firstboot.service CapabilityBoundingSet= User=ferretdb ReadWritePaths=/var/lib/ferretdb/state ProtectHome=yes ProtectSystem=strict NoNewPrivileges=yes

How FerretDB on this image was built:

grep -E '^(toolchain |binary sha256|govulncheck)' /usr/share/doc/ferretdb/BUILD-PROVENANCE.txt
toolchain go1.27.3 linux/amd64, official tarball https://go.dev/dl/go1.27.3.linux-amd64.tar.gz toolchain sha256 65efbc80fdc1fa2cd2631cad5b481777a68e8725f34e89f50fc6ac714e379b40 binary sha256 f554a7edf6280a53ecc1a4ad1905b16d84c52da31b0bdb4fb42b85da2bcdc068 (reproducible: an independent out-of-band build of the same inputs gives the same bytes) govulncheck v1.9.0, source: No vulnerabilities found.; binary: No vulnerabilities found.
10

Changing the administrator password

Set a new password for admin from the MongoDB shell. The passwordPrompt() function asks for it. Then update FERRETDB_PASSWORD in /root/ferretdb-credentials.txt, which ferretdb-shell and the self-test read, or delete the file if you keep the password elsewhere:

sudo ferretdb-shell db.getSiblingDB("admin").updateUser("admin", {pwd: passwordPrompt()})
11

Backup, updates and maintenance

All data is in the PostgreSQL cluster on the data volume, /var/lib/ferretdb/pg17. The simplest backup is an EBS snapshot of that volume, taken with FerretDB and PostgreSQL stopped for a consistent copy (sudo systemctl stop ferretdb postgresql@17-main). For a backup while it runs, dump the PostgreSQL database that holds every FerretDB database:

sudo -u postgres pg_dump -Fc postgres > <backup-dir>/ferretdb-$(date +%F).dump

MongoDB's mongodump and mongorestore (MongoDB Database Tools) also work against FerretDB if you install them.

Ubuntu security updates install automatically. PostgreSQL comes from the PGDG repository. Apply its point releases when you choose, because they restart the database (sudo apt-get update && sudo apt-get install --only-upgrade postgresql-17 postgresql-client-17). FerretDB and the DocumentDB extension are a matched pair and are updated together by a new image version.

FerretDB's settings are in /etc/ferretdb/ferretdb.env (restart FerretDB after a change). PostgreSQL's are in /etc/postgresql/17/main/; the default shared_buffers is 128 MB, and on an m5.large (8 GiB) about 2 GB is a good start (sudo -u postgres psql -c "ALTER SYSTEM SET shared_buffers = '2GB'" and restart PostgreSQL).

Components and paths:

PostgreSQL data directory /var/lib/ferretdb/pg17 (separate EBS volume)

PostgreSQL settings /etc/postgresql/17/main/

FerretDB binary and settings /usr/local/bin/ferretdb, /etc/ferretdb/ferretdb.env

FerretDB state /var/lib/ferretdb/state

Certificate and key /etc/ferretdb/tls/ferretdb.crt, /etc/ferretdb/tls/ferretdb.key

Helpers /usr/local/bin/ferretdb-shell, /usr/local/sbin/ferretdb-tls-cert, /usr/local/sbin/ferretdb-roundtrip.sh

First boot service ferretdb-firstboot.service

Logs journalctl -u ferretdb, /var/log/postgresql/postgresql-17-main.log

Credentials /root/ferretdb-credentials.txt (mode 0600)

Build provenance and licences /usr/share/doc/ferretdb/, /usr/share/doc/postgresql-17-documentdb/LICENSE, /opt/mongosh-2.13.0/

12

Security recommendations

  • Allow port 22 from your admin network only. Keep 27017 on the loopback address. If applications need to connect, open the TLS port 27018 to their subnet only (Step 8), never to the internet.
  • Remember that FerretDB authenticates users but does not yet enforce their roles, so anyone who signs in can read and write everything. Treat every FerretDB password as an administrator password.
  • Give clients the instance's certificate as their CA file, or issue a certificate from your own CA.
  • Change the administrator password if it may have been shared (Step 10), and snapshot the data volume regularly.

Support and licensing

SecureAnyCloud supports this image by email at sales@secureanycloud.com. Include the instance ID, the version (FerretDB 2.7.0 on Ubuntu 24.04) and what you were doing.

FerretDB is open-source software under the Apache License 2.0, the DocumentDB extension is under the MIT License, PostgreSQL is under the PostgreSQL License and mongosh is under the Apache License 2.0. The licences are on the instance. There is no per-user or per-CPU licence fee. The AMI price covers SecureAnyCloud's hardening, first-boot setup and support.

FerretDB is a trademark of FerretDB Inc. MongoDB and MongoDB Compass are trademarks of MongoDB, Inc. PostgreSQL is a trademark of the PostgreSQL Community Association of Canada. Ubuntu is a trademark of Canonical Ltd. All other names are trademarks of their respective owners. SecureAnyCloud is not affiliated with or endorsed by these organisations.

Deploy on AWS

Launch FerretDB 2.7.0 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS