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.
| 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.