← Documentation

Configuration

Everything is an environment variable, read in config/runtime.exs at boot.

One principle runs through all of it: almost nothing raises on a missing variable. A raise in runtime.exs is a crash loop, and a crash loop on a briefly-absent secret is far worse than a feature quietly being off. The two exceptions are noted below, and both are cases where continuing would produce something incoherent.

Required

Variable Notes
DATABASE_URL ecto://user:pass@host:5432/database. Raises if absent in production — there is nothing sensible to do without a database.
SECRET_KEY_BASE 64+ random bytes; mix phx.gen.secret. Raises if absent in production — sessions cannot be signed without it.
PHX_HOST The hostname Miru is served on. Defaults to example.com, which will produce wrong URLs in every email and every ActivityPub identifier.

PHX_HOST deserves emphasis. Every ActivityPub actor id is derived from it, and those identifiers are how remote servers refer to your users. Changing it later means every server that has seen the old identifiers considers those different accounts.

Server

Variable Default Notes
PORT 4077 Not 4000
PHX_SERVER Set to any value to start the web server in a release
POOL_SIZE 10 Database connections
ECTO_IPV6 true or 1 to connect to Postgres over IPv6
DNS_CLUSTER_QUERY For multi-node clustering

Storage

See Storage. In brief: R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_BUCKET, and either R2_ENDPOINT or R2_ACCOUNT_ID. GARAGE_* variants are read as fallbacks. Falls back to LOCAL_STORAGE_DIR when incomplete.

Mail

Variable Default Notes
POSTMARK_API_KEY Without it, no email is sent at all
MAIL_FROM_NAME Miru
MAIL_FROM_ADDRESS noreply@miru.gallery Change this. Sending from a domain you do not control will be rejected.

Email confirmation is required before a user can upload. On an instance with no mail configured, nobody can complete registration — so either configure Postmark or confirm accounts by hand.

There is a recipient allowlist in non-production environments: mail to a domain outside the list is refused rather than sent. This exists so a development instance seeded with realistic data cannot email real people.

Billing

Entirely optional. With no provider configured, the pricing page says paid plans are not open, and every account gets the free tier's allowance. A self-hosted instance normally wants exactly this.

Variable Notes
BILLING_PROVIDER stripe, apple, both, or unset
STRIPE_SECRET_KEY
STRIPE_WEBHOOK_SECRET Verifies webhook signatures over the raw body
STRIPE_PRICE_{FOX,WOLF,BEAR,LION}_{MONTHLY,YEARLY} A tier with no configured price simply cannot be bought
APPLE_PRODUCT_{TIER}_{INTERVAL} Maps App Store product ids to tiers server-side
REQUIRE_CARD_ON_REGISTRATION true requires a card before an account is created, free tier included. Ignored without a Stripe key.

REQUIRE_CARD_ON_REGISTRATION is off by default and should almost certainly stay off on a self-hosted instance. It exists to stop bulk sign-ups on a public service; on an instance you run for yourself and some friends it is friction with no benefit.

Storage quotas

The default allowance for new accounts is an instance setting rather than an environment variable — change it in the admin panel and it applies to accounts created afterwards. Existing accounts keep the quota they were given.

Registration

Also an instance setting: open, invite-only, or closed. Invite-only generates tokens from the admin panel. A closed instance says so on the registration page rather than accepting a form and failing it on submit.