Database Operations¶
If your application uses a PostgreSQL database (database: enabled: true in appmecca.yaml), AppMecca provides commands to back up, restore, and manage your data.
Backups moved. The old
mecca db *subcommand group has been retired entirely — there is nomecca db create,mecca db backup, ormecca db drop. Backups are now an app-level primitive that covers both your database and any drives you declare — see Backups. This page keeps the deeper database context (identity, lifecycle, connection) but the CLI surface for taking and restoring backups lives onmecca app *.
Overview¶
AppMecca creates a managed PostgreSQL database for your app. Your application connects to it using environment variables that AppMecca injects automatically — see Connecting to your database below.
The app-level backup primitive lets you:
- Back up your database + every declared drive as a single atomic bundle
- Restore from a previous bundle (full or partial via
--db-only/--drives-only) - List available bundles for an app
- Download a bundle as pre-signed URLs for off-platform archival
See Backups for the full CLI reference.
Database Lifecycle¶
The database lifecycle is entirely implicit — there are no create or drop commands.
| Moment | What happens |
|---|---|
| Created | On the app's first deploy, if appmecca.yaml sets database: enabled: true. The platform provisions the database, its user, and a generated password, then injects the connection variables into every container. Re-deploying is idempotent — an existing database is reused, never recreated. |
| Backed up | Automatically before every deploy, and on demand with mecca app backup. |
| Dropped | By mecca app delete <app>, which takes a safety backup first. Nothing else drops it. |
# In appmecca.yaml, alongside your containers: and deploy: sections.
# These two lines are the entire opt-in.
database:
enabled: true
Need the database before your app boots?
Run migrations from your container's entrypoint. The database exists and the connection variables are already injected by the time your process starts, so an entrypoint that runs migrations before handing off to your server is the supported pattern. If you need to load an existing dataset instead, use restore from upload — it works on an app you have only registered, before any deploy.
Connecting to Your Database¶
AppMecca injects these variables into every container of a database-enabled app:
| Variable | Contains |
|---|---|
DATABASE_URL |
The full connection string. Prefer this — it is the one value that is always correct. |
DB_HOST |
Database hostname |
DB_PORT |
Port (5432) |
DB_NAME |
Database name |
DB_USER |
Username |
DB_PASSWORD |
Password |
Database and user names are opaque — never construct them
The database and user names are generated hashes such as
am_a1b2c3d4e5f6g7h8. They are not {app}_db / {app}_user and the
naming scheme is not part of the contract. Always read DATABASE_URL (or
the DB_* variables); code that builds a name from the app name will
connect to nothing.
Connection limits¶
Declare how many connections each replica of your app opens:
The platform sums max_connections × replicas across every database-enabled
app before admitting a deploy, and refuses deploys that would exhaust the
shared database. Tune it to match your framework's pool setting (SQLAlchemy
pool_size, Hikari maximumPoolSize, pg.Pool max), and if you genuinely
need more than 50, ask your operator rather than raising the declaration.
Creating a Backup¶
Add -b to target a specific branch. Omit it and AppMecca resolves your
production branch automatically — it does not silently assume main:
The backup runs asynchronously. The command queues it and returns an action ID immediately; it does not wait.
Expected output
Options¶
| Flag | Short | Description |
|---|---|---|
--description "..." |
-d |
Free-form label, shown in mecca app backups |
--branch |
-b |
Git branch (omit to resolve your production branch) |
--quiesce |
Advisory marker only — records intent in the manifest, does not stop your services |
Adding a description¶
Descriptions help you remember why a backup was taken:
Following progress¶
Use the action ID the command printed:
mecca queue status takes an action ID (a full or partial UUID prefix), not
an app name.
Back up before risky changes
Always take a backup before running database migrations, deploying a major version, or making any change that modifies your schema. It takes seconds and can save hours of recovery work.
Listing Backups¶
To see all available backup bundles for an app:
Expected output
Backups: myapp (main)
Last modified Prefix Type
2026-01-15 10:23:45 backups/dev/myapp/main/manual/20260115-102345-pre-v2-migration bundle v1
2026-01-14 08:00:00 backups/dev/myapp/main/pre_deploy/20260114-080000 bundle v1
2026-01-13 08:00:00 backups/dev/myapp/main/pre_deploy/20260113-080000 bundle v1
The Prefix column is what you pass to mecca app restore and
mecca app download. A bundle is a directory of objects, not a single file.
Limiting and filtering¶
# One branch only (omit -b to resolve the production branch)
mecca app backups myapp -e dev -b feature/payments
# Cap the number of rows (default 20, max 200)
mecca app backups myapp -e dev -n 5
# Machine-readable output
mecca app backups myapp -e dev --json
All list-backups options¶
| Flag | Short | Description |
|---|---|---|
--limit |
-n |
Max number of bundles to show (default 20, max 200) |
--branch |
-b |
Git branch (omit to resolve your production branch) |
--json |
Output as JSON (a global flag, available on every command) |
There is no --type or --since filter — the bundle type is shown as part of
each prefix (manual, pre_deploy, pre_drop), so filter with grep if you
need to.
Restoring from a Backup¶
To restore your database to a previous state:
The --force flag skips the confirmation prompt. Without it, you will be asked to confirm.
The restore is asynchronous — the command queues it and prints an action ID:
Expected output
Restore options¶
| Flag | Short | Description |
|---|---|---|
--force |
Skip confirmation | |
--branch |
-b |
Branch to restore into. Required when the app has more than one branch — an omitted branch is refused rather than guessed |
--db-only |
Restore the database, leave drives untouched | |
--drives-only |
Restore drives, leave the database untouched | |
--drives <names> |
Comma-separated subset of drives to restore | |
--in-place |
Skip the safe atomic-swap and extract directly over live drive contents |
Restore replaces everything
A restore resets the existing database and reloads it from the bundle. Any data written after the backup was taken will be lost. Take a fresh backup before restoring, so you have a snapshot of the current state.
A restore is all-or-nothing
The database restore runs as one transaction: if any part of it fails, the database is left exactly as it was, and the error says so. One exception: PostgreSQL large objects. They are backed up with the rest of your data, but the dump commits them in a transaction of its own, after your table data and before your indexes and constraints — so a failure after that point can leave part of the restore applied. The platform checks for exactly this after every failed restore, and the error then says "the restore was NOT atomic … the database's previous contents were replaced and it holds part of the dump" instead of "NOT modified" — restore an earlier bundle. A bundle restore removes your app's large objects with everything else, including those its runtime role owns (see the runtime role), and restores the bundle's. A large object owned by any other role is left alone and can block the restore (the message says so). While it runs, the app's idle database connections are closed and its queries wait until the restore finishes; each waiting query holds one of the database server's connection slots, which are shared with other apps, so restore at a quiet time or scale the app down first. A restore that would need more than half of the database server's lock table is refused before it starts, with the database untouched and a message saying so.
Cross-branch restore
You can restore a bundle taken from one branch into another. This is useful for cloning production data into a feature branch for testing:
Restore workflow¶
A typical restore workflow looks like:
# 1. Take a backup of the current state (just in case)
mecca app backup myapp -e dev -d "before restore"
# 2. List bundles to find the prefix you want
mecca app backups myapp -e dev
# 3. Restore from the target bundle
mecca app restore myapp backups/dev/myapp/main/pre_deploy/20260114-080000 -e dev --force
# 4. Follow it to completion
mecca queue status <action-id> -e dev
# 5. Restart the app so it reconnects to the restored database
mecca app restart myapp web -e dev --force
Downloading a Backup¶
To get a bundle off-platform (for local development or archival):
This prints one pre-signed URL per object, one per line — it does not write any files itself:
Expected output
Fetch them with whatever you like:
| Flag | Description |
|---|---|
--expires-in <seconds> |
URL lifetime. Default 3600 (1 hour), max 86400 (24 hours) |
Using a downloaded backup locally¶
The database half of a bundle is a gzipped SQL dump. To load it into a local PostgreSQL database:
Direct download
The URLs point straight at AppMecca object storage, so the transfer runs between your machine and the storage service. It does not pass through the AppMecca API or a deploy host.
Restoring from a Dump You Uploaded¶
Bringing an existing database onto AppMecca — a local dev DB, a snapshot from
another host, a pg_dump a colleague sent you — is a restore from upload:
you upload a dump and AppMecca replaces the app's database with it.
This replaces every table in the app's database
A safety backup is taken first, so a wrong file is recoverable. But the app loses its database connections for the duration, and in-flight requests will error. Restore into a non-production branch first if you can.
Take the dump¶
Recommended — custom format. It Just Works, whatever roles or grants exist in your source database:
Plain SQL also works, but it must be taken with --no-owner --no-acl:
A plain dump taken with default flags will be refused
A default pg_dump embeds ALTER … OWNER TO <your-local-role> and GRANT
statements. Those roles do not exist on AppMecca's managed database, so the
restore would abort partway through. AppMecca scans the dump and refuses it
up front, before touching your database, with a message telling you to
re-dump. Nothing is lost — just re-dump as shown above.
A plain dump is loaded as data and schema, never as a psql script
AppMecca loads a plain dump with psql's backslash commands switched off, so
a line such as \! … or \i … is refused by psql and nothing after it
runs; the restore then fails and your database is rolled back from the
safety backup, and the error names the first line of your file that begins
with a backslash. A dump made by pg_dump never contains such commands:
text in your data, function bodies or comments that happens to start a line
with a backslash is restored normally and is never refused. A file that
joins several pg_dump outputs together is refused before your database is
touched — upload one dump per restore. The custom format (pg_dump -Fc)
cannot carry psql commands at all, which is another reason it is
recommended.
Restore it¶
Open your app in the dashboard → Database tab → choose the file → confirm (you type the app name) → watch the streaming log.
Or from the CLI:
It asks you to confirm, then to type the app name, uploads the file, and queues
the restore. Add -b to choose the branch whose database is replaced. Omit
it and AppMecca uses the app's only branch — and the confirmation names it. An
app with more than one branch is refused until you pass -b, because guessing
which database to replace is the one mistake that cannot be undone:
| Flag | Short | Description |
|---|---|---|
--file |
The local dump to upload (custom -Fc format recommended) |
|
--branch |
-b |
Branch whose database is replaced. Required when the app has more than one branch — an omitted branch is refused rather than guessed |
--quiesce |
Mark the pre-restore safety backup as quiesced (advisory — your app keeps running) | |
--force |
Skip the yes/no prompt. The typed app name is still required |
The dump uploads straight into AppMecca object storage — it never passes through the AppMecca API.
Works even if the app has never been deployed
You can restore into an app you've only registered — the database
doesn't have to exist yet. AppMecca provisions it on demand as part of the
restore, so there's no "deploy once first" step. Restore is branch-aware:
pick the branch in the dialog, or pass -b on the CLI, to target a
specific branch's database.
Checking on it¶
The restore runs on the platform, so you can close your terminal. The dashboard streams the log live and shows the result summary below.
From the CLI, the result summary — per-table row counts, and whether it rolled back — is one command away once the restore finishes:
You can also follow the queue row:
That shows state, error, and which host claimed it — useful when a restore has not started yet or has failed outright.
What you get back¶
On success, the result lists every restored table with its record count:
(approx) means the exact count took longer than five seconds and this is
PostgreSQL's own row estimate — not a sign that rows are missing.
If it says it rolled back
A restore that fails partway is rolled back from the safety backup it took first, and the dashboard says so above the table list. When that happens the counts you see are the safety backup's, not your dump's — your database is as it was before you started.
A count marked approx means the exact count(*) exceeded a 5-second budget
and AppMecca reported the planner's estimate instead — the data is there, only
the number is approximate.
Limits and rules¶
| Max size | 5 GB |
| Formats | -Fc custom (recommended), plain .sql, gzipped plain .sql.gz |
| Not accepted | a gzipped custom dump (.dump.gz) — upload the .dump directly |
| Extensions | the dump's extensions are created for you if they are on the platform allowlist (citext, pgcrypto, uuid-ossp, hstore, pg_trgm, ltree, …). Anything granting network or filesystem access (dblink, postgres_fdw, …) is refused — ask your operator. |
| Postgres version | a dump from a newer major than the server is refused; older is fine |
If something goes wrong¶
- Refused before anything happened — bad format, forbidden statements, a disallowed extension, or a too-new Postgres. Your database is untouched. Fix the dump and retry.
- Failed and rolled back — the dump applied partway then errored; AppMecca restored the safety backup. Your database is back as it was.
- "the app kept reconnecting and held locks" — your app reopened its
connection pool faster than the restore could take its locks. Your database is
untouched. Scale the app to zero, then retry — that is the only way to
release the app-held locks (the
--quiesceflag is advisory and does not stop the app). - Both the restore and the rollback failed — rare, and AppMecca stops immediately rather than making it worse. The message names the safety bundle; restore it from the Backups tab. Your uploaded dump is kept too.
Deploys are blocked while a restore runs
A deploy started mid-restore could run migrations against a half-restored database, so the dashboard disables the Deploy button until the restore finishes.
How Backups Work¶
Behind the scenes:
- You run
mecca app backup, which queues a backup action and returns its ID. - A deploy agent claims the action.
- The agent runs
pg_dumpagainst your database and archives every declared drive. - Each piece is compressed and written into AppMecca object storage under the bundle prefix.
- A
manifest.jsonis written last, recording checksums and byte counts — its presence is what marks the bundle complete and restorable.
Bundles live under a backups/{env}/{app}/{branch}/… prefix and persist until
the retention policy prunes them, or the environment is torn down.
Next Steps¶
- Backups -- The full bundle reference, retention, and point-in-time recovery
- Service Management -- Check status, view logs, restart services
- Domain Routing -- Understand how URLs map to your services
- Troubleshooting -- Solutions to common database problems