Deploy with Docker Compose
Run the whole platform on a single machine with Docker Compose: Postgres, the Rails API, the background worker and the web app, all from one file in the repo.
The repository ships a docker-compose.yml at its root. It is the quickest way to self-host Messy: one command pulls the published release images and starts everything. The only external service you need is an outbound email route (SMTP or AWS SES) so magic-link logins can reach your inbox.
What you get
- db · Postgres 14. Solid Queue and Solid Cable are database-backed, so there is no Redis.
- backend · the Rails API on
http://localhost:8081. Runs migrations automatically on boot. - worker · the Solid Queue job processor (deliveries, campaigns, scheduled tasks). Same image as the backend.
- frontend · the web app on
http://localhost:8080.
Data lives in two named volumes: pgdata for the database and storage for uploaded files, shared by the backend and the worker.
Start it
You need Docker Engine with the Compose plugin (Docker Desktop includes it) and roughly 2 GB of free memory.
git clone https://github.com/erip-me/messy.git
cd messy
cp .env.example .env
echo "SECRET_KEY_BASE=$(openssl rand -hex 64)" >> .env
docker compose up -dThat pulls two images from GitHub Container Registry and is usually running in about a minute. Once docker compose ps shows the backend as healthy, open http://localhost:8080 and create your account through the sign-up form.
Pick a version
Compose runs :latest unless you say otherwise, which means a docker compose pull can move you to a new release. For anything you rely on, pin the tag in .env so upgrading stays a decision you make:
MESSY_VERSION=v1.0.0Releases are listed on GitHub, and the images are ghcr.io/erip-me/messy-backend and ghcr.io/erip-me/messy-frontend. Both are public, so no registry login is needed.
To run your own build of the working tree instead of a release, add --build: docker compose up -d --build. That compiles gems and the widget bundle and takes a few minutes, and it is what you want once you start changing code.
Outbound email
System mail (signup verification, magic-link logins) is sent over SMTP when SMTP_ADDRESS is set, and over AWS SES otherwise. Any relay works: your provider’s SMTP endpoint, a Mailgun/Postmark/SES SMTP interface, or a local catcher like Mailpit while you evaluate.
EMAIL_FROM=messy@yourdomain.com
SMTP_ADDRESS=smtp.eu.mailgun.org
SMTP_PORT=587
SMTP_USERNAME=postmaster@yourdomain.com
SMTP_PASSWORD=...This route is only for the platform’s own mail. The messages your account sends to customers go through the provider integrations you configure in the app under Channels; see Bring your own keys.
Configuration
Everything is set through .env, read by Compose at start. SECRET_KEY_BASE is required; the rest has working local defaults.
| Field | Type | Description |
|---|---|---|
POSTGRES_PASSWORD | string | Password for the bundled Postgres. Change it before exposing anything. |
API_URL | string | URL browsers use to reach the API. Default http://localhost:8081. |
FRONTEND_URL | string | The app's own URL, also the allowed CORS origin. Default http://localhost:8080. |
SECRET_KEY_BASEreq | string | Session and JWT signing secret. Generate with: openssl rand -hex 64. |
EMAIL_FROM | string | From-address for system mail. |
SMTP_ADDRESS / SMTP_PORT / SMTP_USERNAME / SMTP_PASSWORD | string | SMTP relay for system mail. Leave SMTP_ADDRESS empty to use SES instead. |
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_REGION | string | SES credentials, used when SMTP_ADDRESS is unset. |
SECRET_KEY_BASE signs sessions and login tokens, so treat it like a password: unique per install, never reused from an example. Compose refuses to start without it.Health, backups and upgrades
- Health · the backend answers
GET /up;docker compose psshows it as a health check, anddocker compose logs -f backend workertails the app. - Backups ·
docker compose exec db pg_dump -U messy messy > messy.sql, plus a copy of thestoragevolume for uploads. - Upgrades · on a pinned version, set the new tag in
.env, thendocker compose pull && docker compose up -d. Onlatest, the pull is enough. Building from source instead:git pull && docker compose up -d --build. Migrations run on boot either way.
Serve real traffic
To serve real traffic, put a reverse proxy with TLS (Caddy, nginx, Traefik) in front of ports 8080 and 8081, point two hostnames at them, and set API_URL and FRONTEND_URL to those https URLs in .env, then docker compose up -d again. For a cluster instead of a single box, use the Terraform deployment.