Self-hosting Parletto
What Parletto needs to run on your own server, and how to run it with Docker.
Parletto is a Ruby on Rails app with a React front end and SQLite for storage, so one Docker container on one server is enough to run it. Uploaded files are stored on local disk by default, with Cloudflare R2 as an option.
The source is opening shortly under the MIT license. The steps below run from a checkout of it
(the parletto-core directory). If you would rather not run Parletto
yourself, the managed service at
app.parletto.com is the same software.
What you need
- A server with Docker. You don’t need Ruby or Node on the server.
- No Redis or external database. SQLite holds the app data, cache, jobs and WebSockets.
- A Docker volume for
/rails/storage, where Parletto keeps everything it stores. - For HTTPS on a real server: a DNS name pointing at the server, or a reverse proxy that terminates TLS.
Quick start with Docker Compose
From the source checkout, copy .env.example to
.env, add a SECRET_KEY_BASE, and build and
start the container:
cd parletto-core
cp .env.example .env
echo "SECRET_KEY_BASE=$(openssl rand -hex 64)" >> .env
docker compose up -d --build SECRET_KEY_BASE signs sessions and cookies. Keep it stable, because
changing it signs everyone out.
Open http://localhost:3000 and sign up. A fresh database has no accounts, so the first person to
sign up creates the first organization. To use a different port on the host, set
PARLETTO_PORT (it defaults to 3000).
Once the source is public, you won’t need a checkout: a prebuilt
linux/amd64 image is published as
ghcr.io/grafficmedia/parletto-core:latest, and
docker-compose.yml uses it, so docker compose up -d
(without --build) next to the compose file and your
.env is enough. On an arm64 host, keep building from source.
The image runs Rails behind Thruster, which serves HTTP on port 80 inside the container. When the
container starts, bin/docker-entrypoint runs
bin/rails db:prepare, which creates or migrates the SQLite databases.
The Compose file also sets SOLID_QUEUE_IN_PUMA=true, so background jobs
run inside the web process, and health-checks the container on /up,
which returns 200 once the app has booted.
Keep the storage volume
Everything Parletto stores lives in /rails/storage inside the container:
- the SQLite databases (
production.sqlite3, plus the cache, queue and cable databases), - uploaded avatars, organization icons and attachments, unless R2 is configured,
- Thruster’s TLS certificates.
With Docker Compose this is the parletto_storage volume, so your data
survives docker compose down and image rebuilds. If you run the container
by hand, mount a named volume there (-v parletto_storage:/rails/storage),
or your data is lost when the container is replaced.
HTTP or HTTPS
Production forces HTTPS, so sign-in redirects to https:// even when you
start on plain HTTP. .env.example sets
DISABLE_SSL=true so the app works over plain HTTP on localhost. Use it only
for a trial on http://localhost or behind a proxy that serves the app over plain HTTP.
On a real server, delete that line and serve HTTPS. Either set
TLS_DOMAIN so Thruster gets a Let’s Encrypt certificate (this needs ports
80 and 443 published, so edit the ports in docker-compose.yml), or leave
TLS_DOMAIN unset and let a reverse proxy in front of the container
terminate TLS.
Environment variables
| Variable | Required | What it does |
|---|---|---|
RAILS_MASTER_KEY | With credentials | Decrypts config/credentials/production.yml.enc. Use the contents of production.key. |
SECRET_KEY_BASE | Without credentials | Signs sessions and cookies. It overrides secret_key_base in the credentials. Keep it stable, because changing it signs everyone out. |
DISABLE_SSL | No | Set to true to stop forcing HTTPS. Use it only for a trial on http://localhost or behind a proxy that serves the app over plain HTTP. |
TLS_DOMAIN | Recommended | Domain(s) Thruster gets a Let’s Encrypt certificate for. Leave it unset if a reverse proxy in front of the container terminates TLS. |
SOLID_QUEUE_IN_PUMA | Recommended | Set to true to run background jobs (emails and the like) inside the web process. Otherwise run bin/jobs in a second container with the same volume. |
ACTION_MAILER_HOST | For email | Host used in links in invitation and password-reset emails. Defaults to example.com. |
RAILS_LOG_LEVEL | No | Defaults to info. |
WEB_CONCURRENCY / RAILS_MAX_THREADS / JOB_CONCURRENCY | No | Puma workers, threads per worker, and Solid Queue processes. |
Secrets such as the R2 and SMTP settings live in an encrypted Rails credentials file
(config/credentials/production.yml.enc), not in environment variables.
Rails decrypts it with RAILS_MASTER_KEY. Runtime settings such as
ACTION_MAILER_HOST go in .env or your
platform’s environment. Never commit .env or the key.
Uploads: local disk or Cloudflare R2
R2 is optional. Parletto stores uploads in R2 when the credentials contain
r2.bucket, and on local disk in /rails/storage
when they don’t. Everything works either way.
On local disk, the app serves uploaded files (avatars, organization icons, attachments) itself at
/uploads/..., and only to signed-in members of the file’s organization.
With placeholder r2 values, uploads fail, so leave the
r2 section out if you don’t have a bucket.
To store uploads in R2, create an R2 bucket and API token and put the real values in the
r2 section of the credentials. SMTP settings for invitation and
password-reset emails go in the same file. Then mount
production.yml.enc into the container (with Compose, uncomment the
production.yml.enc line under volumes in
docker-compose.yml) and set
RAILS_MASTER_KEY in .env. The README in the
source walks through creating the credentials file and serving uploads from a CDN.
Upgrading
Take a backup of the storage volume first. Migrations run automatically when the container starts,
because the entrypoint runs bin/rails db:prepare.
With Docker Compose, from source
git pull origin main
docker compose up -d --build With Docker Compose, prebuilt image
Once the image is public, pull the new image instead of rebuilding.
Images are published only for releases: latest is the newest release,
and each release is also tagged with its version, such as v0.1.0.
docker compose pull && docker compose up -d With docker run
git pull origin main
docker build -t parletto .
docker stop parletto && docker rm parletto
docker run -d --name parletto ... # same flags, volume and credentials as before