A managed database is PostgreSQL, MySQL (Percona Server), MariaDB or Valkey (Redis-compatible) run by FFxF in one of your private networks: backed up continuously, restorable to any minute of the last 7 days, updated in a window you choose, with a publicly trusted certificate. You get a connection string; FFxF keeps the engine, the system and the backups running. Managed databases are in beta, open to every account.
Create a database
curl -s https://api.ffxf.net/v1/databases/options -H "Authorization: Bearer $FFXF_TOKEN"
curl -s -X POST https://api.ffxf.net/v1/databases \
-H "Authorization: Bearer $FFXF_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"name": "orders", "vpc": 12, "size_gb": 1}'
{ "data": { "id": 7, "name": "orders", "status": "pending", "engine": "postgresql", "version": "18", "region": "montreal", "size_gb": 1,
"connection": { "host": "private-k3v9x2m1qa.db.ffxf.cloud", "port": 5432, "database": "defaultdb", "username": "ffxfadmin", "ssl_mode": "verify-full" } } }
"engine" picks postgresql (the default), mysql,
mariadb or valkey; GET /databases/options lists
their versions, the sizes with their hourly price (the same for every engine), and the
extensions. Billing starts now, by the hour,
like a machine: the account needs 24 hours of credit. The database is ready within a few
minutes, when status goes from pending to
active. It comes with an admin user, ffxfadmin, and a logical
database, defaultdb (Valkey: the admin default, and no logical
database).
Engines
| Engine | Versions | Port | TLS, as the client sets it |
|---|---|---|---|
| PostgreSQL | 18, 17 | 5432 | sslmode=verify-full&sslrootcert=system |
| MySQL (Percona Server) | 8.4 | 3306 | --ssl-mode=VERIFY_IDENTITY |
| MariaDB | 11.8 | 3306 | --ssl-verify-server-cert |
| Valkey (Redis-compatible) | 8.1 | 6379 | rediss://, or --tls for valkey-cli and redis-cli |
Every engine gets the same backups, forks, public access, maintenance window and disk. Backups differ for Valkey (hourly snapshots, see below); users and logical databases differ for MySQL, MariaDB and Valkey.
Connect
curl -s https://api.ffxf.net/v1/databases/7/credentials -H "Authorization: Bearer $FFXF_TOKEN"
{ "data": { "users": [ { "username": "ffxfadmin", "admin": true, "password": "…", "connection_uri": "postgresql://ffxfadmin:…@private-k3v9x2m1qa.db.ffxf.cloud:5432/defaultdb?sslmode=verify-full&sslrootcert=system" } ],
"connection_uri": "postgresql://ffxfadmin:…@private-k3v9x2m1qa.db.ffxf.cloud:5432/defaultdb?sslmode=verify-full&sslrootcert=system",
"public_connection_uri": null } }
Machines of the private network reach the database at connection.host,
on the engine's port. TLS is always required, with a Let's Encrypt certificate renewed for
you: sslmode=verify-full checks both the certificate and the name.
sslrootcert=system needs libpq 16 or later; with an older client, point
sslrootcert at the system bundle
(/etc/ssl/certs/ca-certificates.crt on Debian and Ubuntu).
mysql --host=private-k3v9x2m1qa.db.ffxf.cloud --user=ffxfadmin -p --ssl-mode=VERIFY_IDENTITY defaultdb
valkey-cli -u "rediss://default:$VALKEY_PASSWORD@private-k3v9x2m1qa.db.ffxf.cloud:6379" PING
Passwords only come out of GET …/credentials and of the calls that create
one, under the databases.write scope: a monitoring key with
vms.read sees the database but cannot log in. Give the connection string to
your application through an environment variable or a secret store, never in code.
Users, databases and extensions
curl -s -X POST https://api.ffxf.net/v1/databases/7/users \
-H "Authorization: Bearer $FFXF_TOKEN" -H "Content-Type: application/json" \
-d '{"username": "app"}'
curl -s -X POST https://api.ffxf.net/v1/databases/7/databases/defaultdb/extensions \
-H "Authorization: Bearer $FFXF_TOKEN" -H "Content-Type: application/json" \
-d '{"extension": "vector"}'
A new user gets a generated password, returned once by the call (and readable later with
…/credentials); it is live within seconds. Names are lowercase letters,
digits and _; up to 30 users and 30 logical databases. The admin is not a
superuser, but owns the logical databases. On PostgreSQL 15 and later, a new user cannot
create tables in the public schema until the admin allows it:
GRANT CREATE ON SCHEMA public TO app;
Extensions come from a list: vector (pgvector), postgis,
pg_trgm, pgcrypto, uuid-ossp, hstore,
citext, unaccent, ltree and a few more, all listed
by GET /databases/options. Removing one does not cascade: if objects still
depend on it, it stays, and the database's warning says why.
MySQL and MariaDB
Users and logical databases are created through the API or the console, not in SQL. Every
user has all rights in every logical database, and the admin can pass them on
(GRANT). The admin has no SUPER, FILE or
CREATE USER, and no rights on the mysql schema. There are no
extensions. Slow queries come from performance_schema, values replaced by
?, without the user.
Valkey
The admin is default: a client that only sends a password
(AUTH <password>, rediss://:<password>@…) connects as
it. Other users are ACL users: every key, every channel and every data command, including
KEYS, FLUSHALL, SORT, INFO and
CONFIG GET. Server commands (CONFIG SET, ACL,
SAVE, SHUTDOWN, MODULE, MONITOR…) are
refused. There is nothing to create for databases: the 16 numbered ones
(SELECT 0 to 15) are open to every user.
curl -s -X PATCH https://api.ffxf.net/v1/databases/9 \
-H "Authorization: Bearer $FFXF_TOKEN" -H "Content-Type: application/json" \
-d '{"eviction_policy": "allkeys-lru"}'
About 60 % of the size holds the data (data_memory.max_bytes); the rest runs
the system and the snapshots. eviction_policy says what happens when it is
full: noeviction, the default, refuses writes and keeps everything;
allkeys-lru removes the least used keys, for a cache; the
volatile-* policies only remove keys that expire. Every write reaches the disk
within a second. Off-site, a snapshot is taken every hour when there were writes: all are
kept 24 hours, then one a day for 7 days. A restore brings back the last snapshot taken
before the chosen minute. The slow queries are the commands with the most total time
since the engine started, and statistics count commands rather than commits.
Backups and restore
Backups are automatic and included: a full backup every week, a differential every day,
and the transaction log archived continuously. They are encrypted before they leave the
database and kept 7 days, outside the server that runs it. Every week, the latest full
backup is restored as a test and checked (backups.last_restore_test).
curl -s -X POST https://api.ffxf.net/v1/databases/7/restore \
-H "Authorization: Bearer $FFXF_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"at": "2026-10-14T09:30:00Z", "name": "orders-before-migration"}'
A restore never overwrites: it creates a new database, billed like any
other, holding the data as it was at that minute, with the same users and passwords. Any
minute of backups.restore_window works (GET …/backups). A
deleted database keeps its backups 7 days, so it can still be restored that way. Before
a risky migration, POST …/backups starts a backup on the spot.
Forks
curl -s -X POST https://api.ffxf.net/v1/databases/7/forks \
-H "Authorization: Bearer $FFXF_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"name": "orders-staging"}'
A fork is a new database whose data is an instant copy of the source, whatever its size:
a staging copy, a test of a migration, an analysis that must not load production. It
starts linked to its source: no backups of its own, and the source
cannot be deleted. After 7 days, or with POST …/detach, it gets its own copy
of the data (about 30 seconds of downtime) and its backups start.
Change size
curl -s -X POST https://api.ffxf.net/v1/databases/7/resize \
-H "Authorization: Bearer $FFXF_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"size_gb": 2}'
Moves the database to a larger size from GET /databases/options: more vCPU,
memory and included disk. Data, users, names and addresses stay the same. The database
restarts: one to two minutes of downtime, during which status is
maintenance and resize.to_gb the size asked for; then it is
active with its new size_gb. Engine settings that depend on memory
(PostgreSQL buffers, the InnoDB buffer pool, Valkey data memory) follow. The new rate applies
from the next hour. Larger sizes only: the disk does not shrink. Also in the console, under
Settings.
Public access
curl -s -X PUT https://api.ffxf.net/v1/databases/7/public-access \
-H "Authorization: Bearer $FFXF_TOKEN" -H "Content-Type: application/json" \
-d '{"enabled": true, "sources": ["203.0.113.10", "2001:db8::/48"]}'
Off at creation. Once on, the database also answers at
public_access.host on its own port (kept for its whole life), over IPv4 and
IPv6, TLS and password still required. sources restricts who may connect;
omitted it is left as is, null or empty lets everyone in. Changes apply
within about 30 seconds. Prefer the private network whenever the client runs at FFxF.
Day to day
| Action | Call | Good to know |
|---|---|---|
| Statistics | GET /databases/{id}/metrics?range=24h | CPU, memory, connections and commits per minute; 1h, 24h or 7d, kept 7 days. |
| Slow queries | GET /databases/{id}/queries | The 20 queries with the most total time, normalized ($1, $2… or ?); on Valkey, per command. |
| Eviction (Valkey) | PATCH /databases/{id} with eviction_policy | Applied within seconds; noeviction by default. |
| Maintenance window | PATCH /databases/{id} with maintenance_day and maintenance_hour | Sunday 3 a.m. Montréal time by default, 2 hours. Minor updates are applied then, usually with a minute or two of downtime. |
| Disk | PATCH /databases/{id} with disk_cap_gb | The disk grows by itself at 80 % full, up to the cap. At the cap, an e-mail warns you; nearly full, the database turns read-only until the cap is raised or data deleted. |
| Size | POST /databases/{id}/resize with size_gb | Larger sizes only; one to two minutes of downtime, new rate from the next hour. |
| Delete | DELETE /databases/{id} | Billing stops; the backups stay 7 days. Refused while a linked fork depends on it. |
Limits and billing
Three databases per account by default, more on request from the console. A database is billed by the hour according to its size; disk beyond what the size includes costs 0.15 CAD per GB per month, billed by the hour too. Backups, restores, the weekly restore test and public access are included.
Useful details
Reading needs vms.read (no passwords); creating, restoring, forking and resizing need
vms.create and an Idempotency-Key; deleting needs
vms.destroy; detaching a fork needs vms.action; a backup on
demand needs backups.write; public access needs network.write.
Passwords, users, logical databases, extensions and settings need
databases.write. The MCP server exposes the same actions: see
MCP tools.