DevelopersManaged databases
Managed databases
Managed databases run on Hostwolf database servers, separate from your app servers. Your apps reach them over TLS, so every copy of an app, on any server and in any city, uses the same data. That is what lets the app tier scale out.
- Engines: PostgreSQL, MariaDB (MySQL-compatible) and Redis.
- Security: TLS is required, and a database only accepts connections from your team's servers.
- Connection pooling: PostgreSQL and MariaDB come with a pooler, so thousands of app connections share a few database connections.
- Storage: grows by itself; storage above the plan is billed per GB.
- Backups: nightly, kept for 7 days, stored off-site. PostgreSQL also has point-in-time recovery for the last 7 days.
- Deleting: billing stops at once; support can restore a deleted database for 7 days.
- Billing: hourly (the monthly price ÷ 730), on your next server invoice. You need an active server subscription first.
Plans and prices#
hw $API/managed-databases/catalog | jq
| Plan | Memory | Storage | App connections (pooled) | Direct connections | Server |
|---|---|---|---|---|---|
small | 256 MB | 5 GB | 1,000 | 10 | shared |
medium | 512 MB | 10 GB | 2,000 | 40 | shared |
large | 4 GB | 80 GB | 5,000 | 100 | dedicated |
xlarge | 12 GB | 200 GB | 10,000 | 200 | dedicated, 8 vCPU |
The catalog lists the regions (for example vultr:ams for Amsterdam) with the monthly and hourly price of each plan, in USD, and the limits above (pooled_clients, direct_connections, pool_size). Pick the region closest to your app servers. Prices on 11 October 2026:
| Plan | Amsterdam (vultr:ams) | Newark (vultr:ewr) |
|---|---|---|
small | $3.88/month | $3.88/month |
medium | $7.74/month | $7.74/month |
large | $62.13/month | $62.13/month |
xlarge | $229.24/month | not sold |
Create and connect#
DB=$(hw -X POST $API/managed-databases \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"engine": "postgres", "plan": "small", "region": "vultr:ams"}' | jq -r .uuid)
# A few minutes later: status goes creating → ready
hw $API/managed-databases/$DB | jq -r .status
# Save the connection URLs on an app, then redeploy it
hw -X POST $API/managed-databases/$DB/attach -d "{\"application_uuid\": \"$APP\"}"
hw -X POST $API/applications/$APP/start
attachsaves the pooled URL asDATABASE_URLand the direct URL asDATABASE_DIRECT_URLon the app (Redis:REDIS_URL). The URLs never pass through your script.attachnever replaces a variable silently. When the app already hasDATABASE_URL(orREDIS_URL) with another value, nothing is saved and the answer is409withconflictsand a freesuggested_env_key. Send"env_key": "DATABASE_URL_2"to save under that name (the direct URL becomesDATABASE_DIRECT_URL_2), or"overwrite": trueto replace them.- The
Idempotency-Keyheader (a UUID) makes the call safe to retry: the same key never creates a second database. Using a key again with different settings answers 409. - Database actions are limited to 10 a minute.
Pooled and direct URLs#
Every PostgreSQL and MariaDB database has two URLs to the same data:
Pooled (url, DATABASE_URL) | Direct (direct_url, DATABASE_DIRECT_URL) | |
|---|---|---|
| PostgreSQL | port 6432, PgBouncer in transaction mode | port 5432 |
| MariaDB | port 6033, ProxySQL | port 3306 |
| Connections | up to the plan's pooled limit; above the pool they wait (up to 120 s) instead of failing | the plan's direct limit (10 on small) |
| Use it for | your app: web requests, workers, every copy of the app | migrations, LISTEN/NOTIFY, session settings (SET), advisory locks, temporary tables and long transactions |
Use the pooled URL in your app. With the direct URL, keep the app's pool within the plan's direct limit, counting every copy of the app. Prepared statements work through the pooler. Run migrations with the direct URL, for example DATABASE_URL=$DATABASE_DIRECT_URL npx prisma migrate deploy as the post-deployment command (app settings, Deployment lifecycle). The pre-deployment command runs in the copy that is still running, before the new build starts: it would run the previous release's migrations, and it is skipped on the first deploy.
To read the URLs yourself, the token needs Read sensitive data:
hw $API/managed-databases/$DB/connection | jq
# {"env_key": "DATABASE_URL", "url": "postgresql://…@db-….db.app.hostwolf.net:6432/…?sslmode=require",
# "direct_url": "postgresql://…@db-….db.app.hostwolf.net:5432/…?sslmode=require", "direct_connections": 10}
| Engine | URL format |
|---|---|
| PostgreSQL | postgresql://user:password@host:port/db?sslmode=require |
| MariaDB | mysql://user:password@host:port/db?ssl-mode=REQUIRED |
| Redis | rediss://user:password@host:port/0 |
The host name (db-<id>.db.app.hostwolf.net) stays the same through plan upgrades and moves. Certificates are public (Let's Encrypt), so no CA file is needed: sslmode=verify-full and ssl-mode=VERIFY_IDENTITY work with your system's certificate store, on the pooled and the direct port. Connections from outside your team's servers, including your own computer, are refused. To inspect data, use a shell on one of your servers (the console's Terminal) or a tool running in one of your apps.
Storage that grows#
The database server's disk grows by itself before it fills up, without a restart. Your plan includes its storage (5, 10, 80 or 200 GB). Above that, each whole GB is billed by the hour: $0.13 a month for 1 GB, $1.24 for 10 GB, $12.38 for 100 GB. extra_storage on the database shows the current extra GB and price, and metrics shows its size.
Upgrading a plan#
When a database is busier than its plan in 3 of 4 hours (CPU, connections, or a full Redis), advice says which plan fits and the team admins get an email. Nothing changes until you upgrade. upgrades lists the plans it can change to now:
hw $API/managed-databases/$DB | jq '.upgrades, .advice'
# [{"plan": "medium", "how": "in_place"}, {"plan": "large", "how": "move"}]
hw -X POST $API/managed-databases/$DB/upgrade -d '{"plan": "large"}'
# 422 {"message": "The large plan costs $62.13/month (now $3.88), billed hourly from the change. … Send \"confirm\": true to go ahead.", "confirm_required": true, "quote": {…}}
hw -X POST $API/managed-databases/$DB/upgrade -d '{"plan": "large", "confirm": true}' # 202
in_place:small→mediumkeeps running.large→xlargemoves the disk to a bigger server, with about a minute of downtime.move: the database is copied to another server. It is unavailable during the copy (about 20 seconds for 200,000 rows), then the host name points to the new server.- The new price applies from the change. Plans never go down.
Redis memory#
A Redis database holds at most its plan's memory (256 MB, 512 MB, 4 GB or 12 GB). The eviction policy decides what happens when it is full:
| Policy | When memory is full | Use it for |
|---|---|---|
allkeys-lru | Removes the least recently used keys | caches |
noeviction (default) | Refuses writes with an error; reads keep working | queues, sessions, data that must not disappear |
volatile-lru, allkeys-lfu, volatile-lfu, allkeys-random, volatile-random, volatile-ttl | The other Redis policies | special cases |
Choose it when you create the database ("eviction_policy": "allkeys-lru"), or change it at any time; it applies at once, without a restart:
hw -X PATCH $API/managed-databases/$DB -d '{"eviction_policy": "allkeys-lru"}'
metrics shows the memory used, the maximum and the keys evicted so far.
Backups#
hw -X POST $API/managed-databases/$DB/backups # an extra backup now (202)
Nightly backups run anyway. In the console, Restore points on the database lists them.
PostgreSQL can be restored to any minute of the last 7 days from the console: the restore makes a new database as it was at that moment, with its own URL, billed like any database. The current database keeps running. To restore a MariaDB or Redis backup, write to support with the database uuid and the time.
Deleting#
hw -X DELETE $API/managed-databases/$DB
Billing stops right away. Apps that used it keep the old DATABASE_URL until you change it.
Errors you may see#
| Status | Meaning |
|---|---|
| 403 | Verify your email address in the console first, or the token lacks a permission. |
| 409 | The database isn't ready yet, or the Idempotency-Key was used for a different database. |
| 422 | Unknown engine, plan or region, no active server subscription, an upgrade it can't take now, or confirm missing. |
| 503 | Managed databases are briefly unavailable. Nothing was changed; try again. |