Databases & add-ons
An add-on is a managed backing service — a database or cache — that Vessl runs in its own container next to your app, on the same server, and wires into your app's environment automatically. You don't manage the container, the network, or the credentials by hand.
Available add-ons:
| Add-on | Use for |
|---|---|
| PostgreSQL | Primary relational database (default) |
| MySQL | Relational database (MySQL/MariaDB apps) |
| Redis | Cache, queues, sessions |
| SQLite | File-backed DB on a persistent volume |
Adding an add-on
You can add one while creating the app (the wizard's Add-ons step) or later from the app's Add-ons tab:
- Open your app → Overview → Connected Services → Add.
- Pick the type (e.g. PostgreSQL) and a name.
- Vessl provisions the container, creates a database and user, and injects the connection details into your app's environment on the next deploy.
You can attach several add-ons to one app (e.g. PostgreSQL and Redis) — the picker is multi-select.
Env vars are injected at deploy time. After adding an add-on, deploy the app (or wait for the next push) so the new variables land in the running container.
How credentials reach your app
For your primary database add-on, Vessl injects both its native variables and the names the popular frameworks expect, so most apps connect on the first try without you mapping anything:
- A ready-to-use connection string:
DATABASE_URL(andPOSTGRES_URL/MYSQL_URLstyle aliases). - Discrete parts:
DB_HOST,DB_PORT,DB_DATABASE,DB_USERNAME,DB_PASSWORD. - Framework conventions:
POSTGRES_*/PG*(libpq, node-postgres),MYSQL_*, and Rails-stylePOSTGRES_DATABASE/POSTGRES_USERNAME.
That means Laravel (DB_*), Django/FastAPI (DATABASE_URL or PG*), Rails,
and most Node ORMs (Prisma/Drizzle reading DATABASE_URL) generally work with
no extra configuration.
Add-ons listen on the server's internal Docker network, not the public internet. Your app reaches the database by its injected host; there's no public port to firewall. To connect from your laptop, use an SSH tunnel to the server.
Verifying the connection
Use the Console tab to run a one-off command inside the running container and confirm the database is wired up — for example:
php artisan migrate:status # Laravel
python manage.py dbshell # Django
See Database migrations & seeding for running migrations and seeders on deploy.
PostgreSQL with extensions (pgvector, etc.)
Some apps need Postgres extensions such as vector (pgvector) or
pg_stat_statements. Choose the pgvector Postgres variant when adding the
add-on — it ships an image with the extension available and pre-creates the
common ones, so CREATE EXTENSION in your migrations succeeds.
MySQL notes
- Vessl creates the app's MySQL user with
mysql_native_password, so PHP PDO and older drivers authenticate without thecaching_sha2_passworderror. - MySQL's first boot initialises its data directory and can take a little longer to accept connections than Postgres. Vessl waits for it to become ready before the first deploy continues — a first deploy may pause briefly on the add-on readiness check; that's expected.
Supported add-on combinations
You can attach multiple add-ons to one app, with one rule: only one primary SQL database per app.
| Combination | Supported |
|---|---|
| PostgreSQL only | ✓ |
| MySQL only | ✓ |
| SQLite only | ✓ |
| PostgreSQL + Redis | ✓ |
| MySQL + Redis | ✓ |
| PostgreSQL + MySQL | ✗ — two primary SQL databases not allowed |
| PostgreSQL + PostgreSQL | ✗ — duplicate primary SQL |
If you try to attach a second SQL database to an app that already has one, Vessl will block it with an error. Detach the existing primary first, then attach a different one.
Redis can always be added alongside any SQL database — it doesn't count as a primary database.
Connecting from your laptop (TablePlus, DBeaver, etc.)
Vessl database containers are bound to 127.0.0.1 on the server — they're
never exposed to the public internet. To connect from a local GUI client like
TablePlus, DBeaver, or DataGrip, you tunnel in over SSH.
Everything you need is in one place: open the add-on → Overview tab → the Connection card.
The Connection card
The card shows both connection paths for the add-on:
- Internal — the Docker-network host (e.g.
my-db.<project>.vessl.internal), port, database, username, password (click the eye to reveal), and a ready connection URL. This is what apps on the same server use; it's also what Vessl injects into your app's env. - External — for desktop clients. It gives the host port the add-on is
published on (not always the default — Vessl picks a free port like
5433when5432is taken), and a ready-to-copy SSH tunnel command.
The password is masked by default and revealed on demand (the Variables tab masks it permanently — use the Connection card to read or copy it).
Step 1 — Open the SSH tunnel
Copy the tunnel command from the External section of the Connection card and run it in a terminal — it already has your server IP, SSH user, and the correct host port filled in. It looks like:
ssh -N -L 5433:127.0.0.1:5433 <ssh-user>@<server-ip>
The -N flag keeps the tunnel open without running a remote command. Leave it
running while you're connected.
Step 2 — Connect your client
Point TablePlus / DBeaver / DataGrip at the local end of the tunnel, using the values shown under 2 · Connect your client in the card:
| Field | Value |
|---|---|
| Host | 127.0.0.1 |
| Port | The host port from the card (e.g. 5433) |
| Database | The card's Database |
| Username | The card's Username |
| Password | The card's Password (reveal + copy) |
| SSL | Off (traffic is already encrypted by SSH) |
TablePlus can manage the tunnel for you. In the connection settings choose Over SSH, enter your server's SSH host + user (also shown in the card), and TablePlus opens the tunnel automatically — no need to run the command yourself.
Redis
Redis works the same way — the Connection card gives the tunnel command and the
password (REDIS_PASSWORD). Tunnel to the published port, then point your Redis
client (RedisInsight, TablePlus, etc.) at 127.0.0.1:<port>.
Redis memory & eviction
Redis keeps all its data in RAM, so a cache that grows without a ceiling will hit the container's memory limit and can be OOM-killed. To prevent that, Vessl launches every Redis add-on with a memory cap sized to its RAM limit:
maxmemory≈ 70% of the add-on's RAM limit — leaves headroom for Redis's background-save fork and overhead.maxmemory-policy volatile-lru— when the cap is reached, Redis evicts the least-recently-used keys that have a TTL (typical framework cache and session entries) and never touches keys without an expiry (e.g. queue lists). Your queued jobs are safe; only expirable cache is evicted under pressure.stop-writes-on-bgsave-error no— a failed background snapshot never freezes your app's writes.
Sizing tip: if your working set is larger than ~70% of the RAM limit, Redis will constantly evict and your cache hit-rate drops. Raise the add-on's RAM Limit under Service → Settings → Resources (then redeploy) so the cache fits comfortably. Watch memory in the service's metrics — if it sits near the cap, give it more room.
An existing Redis add-on picks up these settings the next time it is deployed — redeploy the service after changing its RAM limit to apply a new cap.
Data persistence & backups
- Add-on data lives on a persistent host volume, so it survives container restarts, redeploys, and server reboots.
- Backups are available from the add-on's Backups tab — on-demand and scheduled, with restore. See your plan for included backup features.
Removing a database add-on tears down its container and volume. Take a backup first if you might need the data.