Skip to main content

Addons

Addons are managed services that Miren provisions and operates for your app. Instead of running your own database as a service, you configure an addon, and Miren handles the infrastructure — creating the server, injecting connection credentials, and cleaning up when you're done.

Addons vs. Services

AddonsServices
SetupOne line in app.tomlConfigure image, env, disks, scaling
ManagementMiren provisions and managesYou manage the process
CredentialsAutomatically injected as env varsYou configure manually
Best forProduction databases, managed infrastructureCustom software, full control

If you just need a PostgreSQL database for your app, use an addon. If you need custom PostgreSQL extensions or full control over the configuration, run it as a service.

Available Addons

AddonDescriptionSupported VersionsDefault
miren-postgresqlManaged PostgreSQL database14, 15, 16, 17, 1818
miren-mysqlManaged MySQL database8, 99
miren-valkeyManaged Valkey key-value store (Redis-compatible)7, 8, 99
miren-rabbitmqManaged RabbitMQ message broker3, 44
miren-memcacheManaged Memcached in-memory cache1.4, 1.5, 1.61.6

List available addons on your cluster:

miren addon list-available

Installing an Addon

Declare addons in your .miren/app.toml. They're provisioned automatically on deploy:

name = "myapp"

[services.web]
command = "npm start"

[addons.miren-postgresql]
variant = "small"

Via CLI

Attach an addon to an existing app:

miren addon create miren-postgresql:small -a myapp

Version Selection

Each addon uses a default software version (e.g., PostgreSQL 17). You can choose a different version when installing an addon.

Via app.toml

[addons.miren-postgresql]
variant = "small"
version = "16"

Via CLI

miren addon create miren-postgresql:small -a myapp --version 16

The version value is typically a tag from the supported versions listed above (e.g., 16 or 17).

If no version is specified, the addon's default version is used.

Miren validates that the image is accessible in its registry before provisioning begins. If the image cannot be found, the addon create or deploy will fail immediately with a clear error.

Custom images

If you need a custom build of the addon software (e.g., with additional extensions or patches), you can set version to a full OCI image reference:

[addons.miren-postgresql]
variant = "small"
version = "registry.example.com/custom-postgres:16-custom"

When the version value contains :, Miren uses it as the complete image reference instead of appending it as a tag to the addon's default base image. The image must be accessible from the cluster at provisioning time.

Environment Variables

When an addon is provisioned, Miren injects connection credentials as environment variables into your app. These are available to all services in the app. Variables marked sensitive are redacted in miren env list output.

PostgreSQL (miren-postgresql)

VariableDescriptionExample
DATABASE_URLFull connection URL (sensitive)postgres://user:pass@host:5432/dbname
PGHOSTDatabase hostname10.10.0.196
PGPORTDatabase port5432
PGUSERDatabase usernamemyapp
PGPASSWORDDatabase password (sensitive)
PGDATABASEDatabase namemyapp

Most frameworks and ORMs connect automatically using DATABASE_URL.

MySQL (miren-mysql)

VariableDescriptionExample
DATABASE_URLFull connection URL (sensitive)mysql://user:pass@host:3306/dbname
MYSQL_HOSTDatabase hostname10.10.0.196
MYSQL_PORTDatabase port3306
MYSQL_USERDatabase usernamemyapp
MYSQL_PASSWORDDatabase password (sensitive)
MYSQL_DATABASEDatabase namemyapp

Valkey (miren-valkey)

Valkey is wire-compatible with Redis, so Miren injects both VALKEY_* and REDIS_* variables pointing at the same server. Use whichever your client library expects.

VariableDescriptionExample
VALKEY_URL / REDIS_URLFull connection URL (sensitive)redis://:pass@host:6379
VALKEY_HOST / REDIS_HOSTServer hostname10.10.0.196
VALKEY_PORT / REDIS_PORTServer port6379
VALKEY_PASSWORD / REDIS_PASSWORDPassword (sensitive)

RabbitMQ (miren-rabbitmq)

VariableDescriptionExample
RABBITMQ_URLFull AMQP connection URL (sensitive)amqp://user:pass@host:5672/%2F
RABBITMQ_HOSTBroker hostname10.10.0.196
RABBITMQ_PORTBroker port5672
RABBITMQ_USERBroker usernamemyapp
RABBITMQ_PASSWORDBroker password (sensitive)
RABBITMQ_VHOSTVirtual host/

Memcached (miren-memcache)

VariableDescriptionExample
MEMCACHE_URLConnection URLmemcache://host:11211
MEMCACHE_HOSTServer hostname10.10.0.196
MEMCACHE_PORTServer port11211

Inspecting injected variables

You can verify the variables are set on your app:

miren env list -a myapp

Variants

Each addon offers variants that control the resource allocation and architecture:

miren addon variants miren-postgresql

PostgreSQL & MySQL Variants

PostgreSQL and MySQL each offer two variants:

VariantDescriptionUse case
smallDedicated server (1 GB storage)Production apps needing isolation
sharedMulti-app shared serverDevelopment, staging, or small apps

Dedicated (small): Each app gets its own database instance with dedicated storage. Best for production workloads where you need isolation and predictable performance. Start here if your app might grow.

Shared (shared): Multiple apps share a single database server, each with their own logical database and credentials. The shared server does not isolate workloads — a heavy query in one app can affect others on the same server. This variant is designed for small internal tools, staging environments, and apps you know will stay lightweight.

Valkey, RabbitMQ, and Memcached Variants

These addons currently offer only the dedicated small variant — each app gets its own server instance.

If no variant is specified, the default (small) is used.

Changing variants

There is currently no way to migrate between variants (e.g. upgrading from shared to small). If you need to switch, you would need to back up your data, destroy the addon, recreate it with the new variant, and restore. We plan to add in-place variant upgrades in a future release.

Addon Lifecycle

Provisioning

When you deploy an app with addons or run addon create, Miren:

  1. Creates the addon association (status: pending)
  2. Provisions the backing infrastructure (status: provisioning)
  3. Injects environment variables into your app configuration
  4. Marks the addon as ready (status: active)
  5. Starts your app with the injected credentials

Provisioning typically takes 1–2 minutes for a new dedicated server (longer if the PostgreSQL image needs to be pulled for the first time).

Your app won't start until addon provisioning completes — Miren holds off launching your app's processes until all addons reach active status.

Checking Status

List addons attached to your app:

miren addon list -a myapp

Removing an Addon

Remove an addon and delete its data:

miren addon destroy miren-postgresql -a myapp
warning

Destroying an addon permanently deletes the database and all its data. This cannot be undone.

To remove an addon via app.toml, delete the [addons.miren-postgresql] section and redeploy. Miren detects the removal and deprovisions the addon.

Example: Bun + PostgreSQL

A simple web server that tracks page visits using PostgreSQL:

.miren/app.toml:

name = "my-bun-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:

miren deploy

Miren provisions PostgreSQL, injects DATABASE_URL, and starts your app once the database is ready.

Backing Up and Restoring

Early Version

Addon backup and restore uses the general-purpose disk backup system. We plan to add addon-aware backup commands in a future release that will simplify this workflow. For now, the steps below work reliably for PostgreSQL addon data.

The disk backup and disk restore commands must be run directly on the server (via SSH or miren ssh), not from your local machine. Remote backup support is planned.

Each PostgreSQL addon stores its data on a Miren disk. You can back up and restore this disk using the miren disk backup and miren disk restore commands.

Finding the Disk Name

List disks to find the one belonging to your addon:

miren debug disk list

Addon disks are named with a pg- prefix. For dedicated (small) addons, the name includes your app name (e.g. pg-pg-myapp-s...-data). For shared addons, it starts with pg-shared-data-.

Creating a Backup

Back up the disk to a compressed snapshot file. This must be run on the server:

miren disk backup -n <disk-name>

This creates a timestamped .miren.zst file in the current directory. If the disk is currently in use, the backup will be crash-consistent (safe for PostgreSQL, which uses write-ahead logging).

Example:

miren disk backup -n pg-pg-myapp-sCZDabc123-data
# Output: pg-pg-myapp-sCZDabc123-data-20260324-120000.miren.zst

You can specify a custom output path with -o:

miren disk backup -n pg-pg-myapp-sCZDabc123-data -o /backups/myapp-db.miren.zst

Restoring from a Backup

To restore from a backup, provide the snapshot file. This must also be run on the server:

miren disk restore -s <snapshot-file>

The restore procedure recreates the disk with the original name. If the disk already exists, use --force to overwrite:

miren disk restore -s myapp-db.miren.zst --force

To restore to a different disk name:

miren disk restore -s myapp-db.miren.zst -n new-disk-name

After restoring, restart your app to pick up the restored data:

miren app restart myapp

Backup Recommendations

  • Schedule regular backups for production databases, especially before destructive operations like addon destroy
  • Store backups off-server — copy snapshot files to external storage
  • Test restores periodically to verify your backups are valid
  • Backups are compressed with zstd and include checksum verification