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.

terminal
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 -d

That 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:

.env
MESSY_VERSION=v1.0.0

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

Signing up sends a verification email, and logging in works through magic links. Configure SMTP (below) before you create your account, or the mail has nowhere to go.

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.

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

FieldTypeDescription
POSTGRES_PASSWORDstringPassword for the bundled Postgres. Change it before exposing anything.
API_URLstringURL browsers use to reach the API. Default http://localhost:8081.
FRONTEND_URLstringThe app's own URL, also the allowed CORS origin. Default http://localhost:8080.
SECRET_KEY_BASEreqstringSession and JWT signing secret. Generate with: openssl rand -hex 64.
EMAIL_FROMstringFrom-address for system mail.
SMTP_ADDRESS / SMTP_PORT / SMTP_USERNAME / SMTP_PASSWORDstringSMTP relay for system mail. Leave SMTP_ADDRESS empty to use SES instead.
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_REGIONstringSES 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 ps shows it as a health check, and docker compose logs -f backend worker tails the app.
  • Backups · docker compose exec db pg_dump -U messy messy > messy.sql, plus a copy of the storage volume for uploads.
  • Upgrades · on a pinned version, set the new tag in .env, then docker compose pull && docker compose up -d. On latest, 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.