Postgres databases
Velixir runs managed PostgreSQL inside the same K3s clusters that run your apps. Provisioning takes about a minute. Write-ahead logs ship to object storage continuously for point-in-time recovery.
Provision a database
From a project's Databases tab click + New Database. You choose:
- A name for the instance - how it shows in the dashboard. This is not the database name.
- A plan (CPU + memory + disk).
- The PostgreSQL version (17, 16, or 15).
- Optionally a database name and username - both default to
app. They create the initial database and its owner role, and are fixed at creation time (Postgres can't rename the bootstrap database afterwards). - High availability on / off - HA runs a primary plus a synchronous standby replica on separate nodes, with automatic failover.
- External access on / off - reach the instance from outside the cluster (psql, migrations, BI tools). In-cluster app connections work either way.
We use CloudNativePG as the operator. It handles primary election, streaming replication, and backups.
Connecting from your app
The cleanest pattern: bind the database to your app. From the database's detail page, under Bound apps, pick the app and click Bind. Velixir injects the connection string into the app on its next deploy.
// In your .NET code
var connString = Environment.GetEnvironmentVariable("DATABASE_URL");
builder.Services.AddDbContext<MyDbContext>(opt =>
opt.UseNpgsql(connString));
When you bind you choose the env var name (it defaults to DATABASE_URL) and the format - an ADO.NET keyword string for .NET / Npgsql / EF Core / Dapper, or a postgres:// URL for other stacks. Velixir injects exactly that one variable, with the live connection string, on the app's next deploy. To attach a second database, bind it under a different name (e.g. ANALYTICS_DATABASE_URL). If you set an app env var with the same name yourself, your value wins.
Connecting from outside the cluster
For psql / DBeaver / Rider sessions: open the database's detail page and Reveal the external connection string (external access must be enabled on the instance). It routes through the same edge as your web traffic with sslmode=require, so it works from anywhere. The username and password are the same as the in-cluster ones - only the host and port differ.
psql tip: use the URL form of the connection string (
psql 'postgres://...'), not the ADO.NET keyword form - psql doesn't parse the latter. Make sure the database name is on the end of the URL (.../yourdbname); otherwise psql connects to the defaultpostgresmaintenance database instead of yours, and you'll see only the built-in schemas.
Credentials & rotation
Each instance has one role - the username and password you set at creation (default app) - used for both in-cluster and external connections. You can rotate the password anytime from the database's detail page: the old password stops working immediately, and any bound apps pick up the new credential on their next deploy (the dashboard tells you how many to redeploy). Use it for leak response or routine hygiene.
Databases, schemas & tables (note for MySQL users)
PostgreSQL has three levels where MySQL has two: database → schema → table. You connect into one database (the database name you set, default app) and your tables live in a schema inside it. Every database ships with the same built-in schemas - public, information_schema, pg_catalog, pg_toast - and unless you say otherwise your tables go in public. So a table is addressed as public.widgets, not mydb.widgets.
Translating from MySQL:
- A MySQL database (
mydb.widgets) maps to a PostgreSQL schema (public.widgets). To namespace tables the way you'd use separate MySQL databases, create schemas inside one database:CREATE SCHEMA billing;thenbilling.invoices. Cross-schema joins work normally. - A PostgreSQL database is one level up and fully isolated - you can't join across databases in a single connection (that needs
postgres_fdw). Reach for one database, many schemas where you'd have reached for many MySQL databases. - There's no
USE otherdb;- switching databases means a new connection (\cin psql opens one).
Quick checks once connected: SELECT current_database(); (which database you're in), \dn (schemas), \dt (tables on the search path).
Backups & recovery
Backups are handled by CloudNativePG's continuous WAL archiving to object storage, combined with periodic base backups. That's what gives you point-in-time recovery within your plan's retention window (7 days on Starter, 14 on Standard, 28 on Pro/Scale). Need longer? Take your own pg_dump exports - the window covers operational recovery, not archival. The Backups tab shows the backups taken for your instance.
Restore is self-serve from the Backups tab. Pick a backup, optionally enter a point-in-time within your retention window, and Velixir provisions a brand-new database recovered to that point - your current instance is never touched, so you can verify the restore before switching apps over. Because recovery replays the continuously-archived WAL, you can target any moment in the window, not just a backup boundary; the recovered database keeps the same credentials and connection details, just under a new instance name.
Plans
Database plans set per-instance resources (and per-replica resources in HA mode). You can change a plan with about a minute of downtime - we re-create the StatefulSet pod with the new resource limits.