FerretDB 2.7.0 on AWS User Guide
Overview
This guide covers launching and using FerretDB 2.7.0 on Ubuntu 24.04 from cloudimg on Amazon EC2. It is one version of the cloudimg Hardened Apache Cassandra, FerretDB & NoSQL Database AMIs listing on AWS Marketplace; you pick it as the software version when you subscribe and launch. The other databases in the listing are described on the listing's guide hub.
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 image runs FerretDB 2.7.0 on PostgreSQL 17 with DocumentDB 0.107.0 (the release FerretDB 2.7.0 is built for), on one instance.
What the cloudimg image adds:
- No default login, 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
adminwith 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
postgresuser) reach it over the local socket with peer authentication. Itspostgresandferretdbroles have no password, so there is no PostgreSQL credential to steal, and there is notrustrule anywhere inpg_hba.conf. - FerretDB on localhost, with TLS. FerretDB listens on
127.0.0.1:27017and, with a certificate generated on your instance's first boot, on127.0.0.1:27018over TLS. Nothing but SSH is reachable from the network until you choose to open the TLS port to your VPC (Step 8). - A FerretDB built 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, so cloudimg compiles FerretDB 2.7.0 from its release source with Go 1.27.3 and the fixed module releases;
govulncheckreports no vulnerabilities in it. 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
ferretdbuser 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, andmongoshis 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 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 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 open the TLS port to your application subnet later (Step 8).
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick FerretDB 2.7.0 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large, your key pair and a security group as above. The AMI brings two volumes: the root volume and a 20 GiB data volume (/dev/sdf) for /var/lib/ferretdb. Make the data volume as large as your data needs at launch.
With the AWS CLI (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 once the instance is running.
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| FerretDB 2.7.0 on Ubuntu 24.04 | ubuntu |
ssh -i <your-key.pem> ubuntu@<public-ip>
Step 3: Check the services
First boot creates the PostgreSQL cluster, the administrator and the certificate; then PostgreSQL and FerretDB start:
systemctl is-active ferretdb-firstboot postgresql@17-main ferretdb
sudo test -f /var/lib/cloudimg/ferretdb-firstboot.done && echo FIRSTBOOT_DONE
On the first boot all three units are active and the first boot marker is present (on later boots ferretdb-firstboot shows inactive: it runs once):
active
active
active
FIRSTBOOT_DONE
Only SSH listens on a network address. FerretDB listens on the loopback address: 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
Step 4: Get your administrator password
sudo cat /root/ferretdb-credentials.txt
The file is readable by root only:
# 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://www.cloudimg.co.uk/guides/ferretdb-2-7-0-on-ubuntu-24-04-aws/
Step 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 is an interactive shell; 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 them, 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: 'cloudimg'
}
Remove the example database when you have finished with it:
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). Check the FerretDB compatibility documentation for the commands and operators your application relies on before you migrate.
Step 6: Create a user for your application
Give each application its own user, so you can change or revoke its password without touching admin. Run this in sudo ferretdb-shell (passwordPrompt() asks for the new password, so it 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").
Step 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 it (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.
Step 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.
Step 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=cloudimg
/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), writes, reads, indexes and aggregates data, and 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; the key is readable by root and the ferretdb group only:
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 = cloudimg 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.
Step 10: Changing the administrator password
Set a new password for admin from the MongoDB shell (passwordPrompt() 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()})
Step 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).
| Component | Path |
|---|---|
| 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/ |
Step 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: 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
cloudimg supports this image by email at support@cloudimg.co.uk. 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 under the MIT License, PostgreSQL under the PostgreSQL License and mongosh 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 cloudimg'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. cloudimg is not affiliated with or endorsed by these organisations.