Introducing Addons
Most apps need more than just code. They need a database, maybe a cache, maybe a queue. And every deployment platform eventually has to answer the question: how do you get those things?
The usual answers range from “figure it out yourself” to “here’s a managed service that costs more than your server.” Neither felt right for Miren. We wanted something that matched the rest of the experience: declare what you need, deploy, and get on with your day.
Today we’re releasing Addons, and we’re launching with five of the most commonly needed pieces of infrastructure: PostgreSQL, MySQL, Valkey, Memcached, and RabbitMQ.
Just say what you need
Here’s the whole thing. In your .miren/app.toml:
[addons.miren-postgresql]
variant = "small"
That’s it. When you run miren deploy, Miren provisions a dedicated PostgreSQL container, injects the connection credentials as environment variables, and holds off starting your app until the database is ready. Your code picks up DATABASE_URL and goes.
No provisioning scripts. No Terraform. No clicking through a dashboard to copy-paste credentials. You declared a need; Miren took care of the rest.
Variants
Every addon ships with a small variant, and it’s the default if you don’t specify one. small gives you a dedicated container for that addon: your own PostgreSQL server, your own MySQL server, your own Valkey, Memcached, or RabbitMQ instance, with its own storage where applicable. One app, one instance, full isolation. It’s the right choice when your data matters and your workload needs predictable performance.
PostgreSQL and MySQL also offer a second variant, shared, and it’s the more interesting one. When the first app on a cluster requests a shared PostgreSQL addon, Miren provisions a single PostgreSQL container. When a second app requests shared, Miren doesn’t spin up another container — it creates a new database inside the one that’s already running. Each app gets its own database, its own credentials, complete logical separation. But they share the underlying container. MySQL’s shared variant works exactly the same way.
Valkey, Memcached, and RabbitMQ launch with small only. They’re lightweight enough to run dedicated that the multi-tenant math is less interesting, and we’d rather ship the simple thing first and add variants once we see what people actually want.
This is great for the apps that don’t need their own dedicated database container: staging environments, internal tools, side projects, anything ephemeral. Instead of paying the overhead of a full PostgreSQL instance per app, you get a database in seconds on infrastructure that’s already running.
It’s also a good demonstration of why addons exist as a concept. Your app doesn’t say “give me a container.” It says “I need PostgreSQL.” Miren decides what that means based on the variant you chose. Today shared means a multi-tenant container on your cluster. Tomorrow it could mean something else entirely, and your app config wouldn’t change.
What your app sees
Every addon injects the connection details your app needs as environment variables. For PostgreSQL, that looks like:
DATABASE_URL— full connection string, ready for any ORMPGHOST,PGPORT,PGUSER,PGPASSWORD,PGDATABASE— the individual pieces, for frameworks that want them
Most frameworks connect automatically with DATABASE_URL. Here’s a Bun app using the shared PostgreSQL variant:
# .miren/app.toml
name = "my-app"
[services.web]
command = "bun run index.ts"
[addons.miren-postgresql]
variant = "shared"
// index.ts
import { SQL } from "bun";
const sql = new SQL({ url: process.env.DATABASE_URL });
await sql`
CREATE TABLE IF NOT EXISTS visits (
id SERIAL PRIMARY KEY,
visited_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
`;
const server = Bun.serve({
port: process.env.PORT || 3000,
async fetch(req) {
await sql`INSERT INTO visits DEFAULT VALUES`;
const [{ count }] = await sql`SELECT COUNT(*) as count FROM visits`;
return new Response(`Visits: ${count}\n`);
},
});
Deploy with miren deploy and your app has a database. That’s the workflow.
Versions, and when you need something custom
By default, every addon tracks the current stable release and a handful of the previous majors, so you can match whatever version your app already expects. At launch, the supported defaults are:
- PostgreSQL — 14, 15, 16, 17, 18 (default: 17)
- MySQL — 8, 9 (default: 8)
- Valkey — 7, 8 (default: 8)
- Memcached — 1.4, 1.5, 1.6 (default: 1.6)
- RabbitMQ — 3, 4 (default: 4)
You pick which one you want with the version field:
[addons.miren-postgresql]
variant = "small"
version = "16"
If you leave version off, you get the current stable release. If you pin it, Miren honors the pin and won’t upgrade underneath you.
Here’s the part we’re proud of: version isn’t restricted to a version number. It also accepts a full OCI image URI. If you need a PostgreSQL build with pgvector, or a hardened internal MySQL image, or anything else that doesn’t match our default images, point the addon at your own:
[addons.miren-postgresql]
variant = "small"
version = "ghcr.io/my-org/postgres-with-pgvector:17.2"
Miren still handles provisioning, storage, credentials, and wiring. It just runs your image instead of ours. You get the ergonomics of an addon without being boxed in by our defaults.
Built on top of what’s already there
If you’ve been using Miren, you might be thinking: couldn’t I already run PostgreSQL as a service with a disk? Yes — and you still can. Services and disks aren’t going anywhere.
Addons are a layer above them. Under the hood, when you attach a PostgreSQL addon, Miren is doing the same things you’d do manually: creating a sandbox to run the database (just like a service would), attaching a disk for persistent storage, wiring up networking, generating credentials. The difference is that addons handle all of that for you in a single declaration.
Think of it as the difference between assembling the parts yourself and telling Miren what you need. Services and disks give you full control: you pick the image, configure the environment, manage the lifecycle. Addons give you a higher-level interface where Miren makes those decisions based on best practices and the variant you chose. Both are the right tool depending on what you’re doing. If you need a stock PostgreSQL database, an addon gets you there in one line. If you need a custom PostgreSQL build with specific extensions, run it as a service.
Only the beginning
PostgreSQL, MySQL, Valkey, Memcached, and RabbitMQ cover the vast majority of what apps ask for on day one, but the addon system is designed to grow. More addons are already in the works, and the interface will stay the same regardless of what’s behind it: declare a need, pick a variant, deploy.
If you’re already running Miren, addons are available now. Add a few lines to your app.toml and deploy. Check out the addon docs for the full details. If you haven’t tried Miren yet, this is a good excuse to get started.