Self-Hosting Loomio with Docker Compose
Loomio Started as a Tool for Occupy Wall Street — Now It Powers Decisions for Cooperatives, Boards, and Distributed Teams Worldwide
Loomio is a collaborative decision-making platform. Groups propose ideas, discuss them in threads, and reach decisions through structured voting — polls, proposals, ranked choice, score voting, dot voting, and time polls. It’s not a chat app or forum; it’s built specifically for the moment when a group needs to go from discussion to decision. Official site
Organizations like Greenpeace, the Enspiral Network, and dozens of cooperatives use Loomio to make decisions asynchronously without endless email chains or meetings that could have been a poll.
| Feature | Loomio | Typical Forum |
|---|---|---|
| Threaded discussions | Yes | Yes |
| Structured voting/polls | 7 voting types built in | No (or basic polls) |
| Decision outcomes tracked | Yes — with deadlines and results | No |
| Proposals with accept/decline/abstain | Yes | No |
| Email integration | Reply by email | Notifications only |
| Subgroups | Yes — nested groups with permissions | Subcategories |
| Real-time collaboration | Hocuspocus (collaborative editing) | No |
Prerequisites
- A Linux server (Ubuntu 22.04+ recommended)
- Docker and Docker Compose v2+ installed (guide)
- 2 GB RAM minimum (4 GB recommended)
- 10 GB of free disk space
- A domain name with DNS configured (required for email links)
- SMTP credentials (required — Loomio relies heavily on email notifications)
| Requirement | Minimum | Recommended |
|---|---|---|
| CPU | 2 cores | 4 cores |
| RAM | 2 GB | 4 GB |
| Disk | 10 GB | 20 GB+ |
| Required (SMTP) | Required (SMTP) |
Docker Compose Configuration
Loomio runs several services: the Rails web application, a background worker, PostgreSQL, Redis, a Hocuspocus server for collaborative editing, and optionally a reply-by-email service (Haraka).
Create a .env file:
# Loomio environment configuration
LOOMIO_CONTAINER_IMAGE=loomio/loomio
LOOMIO_CONTAINER_TAG=3.0.21
# Domain and scheme
CANONICAL_HOST=loomio.yourdomain.com
SCHEME=https
# Security
SECRET_COOKIE_TOKEN=generate-a-64-char-random-string-here-using-openssl-rand-hex-32
DEVISE_SECRET=generate-another-64-char-random-string-here-use-openssl-rand-hex
# Database
POSTGRES_CONTAINER_TAG=17
POSTGRES_PASSWORD=change-this-strong-password
DATABASE_URL=postgresql://postgres:change-this-strong-password@db/loomio_production
# Redis
REDIS_URL=redis://redis:6379
# SMTP — required for notifications and invitations
SMTP_SERVER=smtp.yourdomain.com
SMTP_PORT=587
SMTP_USERNAME=[email protected]
SMTP_PASSWORD=your-smtp-password
SMTP_DOMAIN=yourdomain.com
SMTP_AUTH=plain
REPLY_HOSTNAME=loomio.yourdomain.com
# Channels — disable unless needed
CHANNELS_URI=
# Admin
SUPPORT_EMAIL=[email protected]
# Features
FEATURES_ENABLE_GROUPS=1
MAX_THREADS=500
MAX_POLLS=300
Now create docker-compose.yml:
services:
app:
image: loomio/loomio:3.0.21
restart: unless-stopped
env_file: .env
environment:
- TASK=app
ports:
- "3000:3000"
volumes:
- uploads:/loomio/public/system
- storage:/loomio/storage
- files:/loomio/public/files
- plugins:/loomio/plugins/docker
- tmp:/loomio/tmp
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000"]
interval: 30s
timeout: 10s
retries: 3
worker:
image: loomio/loomio:3.0.21
restart: unless-stopped
env_file: .env
environment:
- TASK=worker
volumes:
- uploads:/loomio/public/system
- storage:/loomio/storage
- files:/loomio/public/files
- tmp:/loomio/tmp
depends_on:
- app
hocuspocus:
image: loomio/loomio:3.0.21
restart: unless-stopped
env_file: .env
environment:
- TASK=hocuspocus
depends_on:
- app
db:
image: postgres:17
restart: unless-stopped
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: loomio_production
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7.4
restart: unless-stopped
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
volumes:
pgdata:
uploads:
storage:
files:
plugins:
tmp:
redis_data:
Start the stack:
docker compose up -d
Initial Setup
Once containers are running, initialize the database:
# Run database migrations
docker compose exec app rake db:setup
This creates the database schema and seeds initial data. Visit http://your-server-ip:3000 and create the first account — the first registered user automatically becomes the admin.
Navigate to Settings (gear icon) to configure:
- Group name and description — your organization’s name
- Privacy — secret (invite-only), closed (visible but invite-only), or open
- Features — enable/disable threads, polls, subgroups
- Membership — who can invite new members
Configuration
Groups and Subgroups
Loomio’s organizational structure is hierarchical:
- Groups — top-level organization (e.g., “Our Cooperative”)
- Subgroups — nested groups with their own members and permissions (e.g., “Finance Committee”, “Tech Working Group”)
- Threads — discussions within a group
- Proposals/Polls — decision tools within threads
Members can belong to multiple groups and subgroups with different permission levels (admin, member, guest).
Decision Tools
Loomio supports seven voting types:
| Tool | Use Case | How It Works |
|---|---|---|
| Proposal | Binary decision | Agree, Abstain, Disagree, Block |
| Poll | Multiple choice | Choose one or more options |
| Score Poll | Rate options | Score each option 0-10 |
| Ranked Choice | Priority order | Rank options by preference |
| Time Poll | Schedule meeting | Select available time slots |
| Dot Vote | Budget allocation | Distribute limited dots across options |
| Check | Acknowledgment | Simple yes/no confirmation |
Email Integration
Loomio is designed around email notifications. Members receive:
- New thread notifications
- Proposal/poll invitations with voting links
- Decision outcome announcements
- @mentions
Members can vote directly from email links without logging in (token-based voting). This dramatically increases participation for groups where not everyone checks the web interface regularly.
Advanced Configuration
SAML/SSO Single Sign-On
For organizations using identity providers (Okta, Azure AD, Keycloak):
Add to your .env:
SAML_IDP_SSO_TARGET_URL=https://idp.yourdomain.com/saml/sso
SAML_IDP_CERT_FINGERPRINT=your-idp-cert-fingerprint
SAML_ISSUER=https://loomio.yourdomain.com
FEATURES_SAML_LOGIN=1
OAuth (Google, Microsoft, Slack)
Enable social login by adding OAuth credentials to .env:
GOOGLE_APP_KEY=your-google-oauth-client-id
GOOGLE_APP_SECRET=your-google-oauth-client-secret
FEATURES_GOOGLE_LOGIN=1
Webhooks
Loomio can send webhooks to Slack, Mattermost, Microsoft Teams, or custom endpoints. Configure per-group under Group Settings > Integrations.
Reverse Proxy
Behind a reverse proxy, ensure HTTPS is properly terminated. Loomio needs SCHEME=https and CANONICAL_HOST in .env to generate correct email links and callback URLs.
For detailed reverse proxy setup, see Reverse Proxy Setup.
Backup
Critical data to back up:
| Data | Location | Priority |
|---|---|---|
| PostgreSQL database | pgdata volume | Critical — all groups, threads, decisions |
| Uploaded files | uploads + files volumes | Important — attachments, avatars |
| Environment file | .env | Critical — contains secrets |
Back up the database:
docker compose exec db pg_dump -U postgres loomio_production > loomio-backup-$(date +%Y%m%d).sql
For automated backup strategies, see Backup Strategy.
Troubleshooting
Emails not sending
Symptom: Users don’t receive invitation emails or notification emails after signing up.
Fix: Check SMTP configuration in .env. Verify credentials with a test: docker compose exec app rails console then ActionMailer::Base.mail(from: '[email protected]', to: '[email protected]', subject: 'test', body: 'test').deliver_now. Common cause: SMTP_AUTH should be plain for most providers. Check worker logs: docker compose logs worker.
Database migration errors
Symptom: rake db:setup fails with schema errors.
Fix: Ensure PostgreSQL is fully ready before running migrations. Wait for the health check to pass: docker compose exec db pg_isready. If migrating from an older version, run docker compose exec app rake db:migrate instead of db:setup.
Hocuspocus not connecting
Symptom: Real-time collaborative editing doesn’t work in threads.
Fix: The Hocuspocus service needs to be reachable from the browser. If behind a reverse proxy, ensure WebSocket connections are proxied correctly. The service runs on the same port as the main app (proxied internally). Check docker compose logs hocuspocus for connection errors.
High memory usage
Symptom: Server becomes slow or unresponsive after extended use.
Fix: Loomio’s Rails worker can accumulate memory. Set WORKER_COUNT=2 in .env to limit Puma workers. Redis rarely causes issues but can be limited with maxmemory 256mb in a custom redis.conf. PostgreSQL shared buffers default to 128 MB — reduce with shared_buffers=64MB for small instances.
Reply-by-email not working
Symptom: Replies sent to notification emails don’t appear in Loomio threads.
Fix: Reply-by-email requires a mail server receiving inbound email. The official deploy includes Haraka (SMTP receiver) on port 25. Ensure port 25 is open on your server, DNS MX records point to your domain, and REPLY_HOSTNAME matches your domain. Many VPS providers block port 25 by default — check with your provider.
Resource Requirements
| Component | RAM (idle) | RAM (active) | CPU | Disk |
|---|---|---|---|---|
| App (Rails/Puma) | ~300 MB | ~500 MB | Low-Medium | Minimal |
| Worker (Sidekiq) | ~200 MB | ~400 MB | Medium | Minimal |
| Hocuspocus | ~80 MB | ~150 MB | Low | Minimal |
| PostgreSQL | ~100 MB | ~300 MB | Low-Medium | Grows with content |
| Redis | ~30 MB | ~80 MB | Minimal | Minimal |
| Total | ~710 MB | ~1.4 GB | Low-Medium | 10 GB+ |
Verdict
Loomio fills a niche that almost nothing else does: structured group decision-making. If your organization, cooperative, board, or community needs to move from “we discussed it” to “we decided it” — with a documented outcome, a deadline, and a clear record of who supported what — Loomio is the right tool.
It’s not a replacement for Slack (real-time chat), Discourse (forum), or Nextcloud (file sharing). It’s specifically for the decision-making step that those tools lack. The voting tools are genuinely good — ranked choice, score polls, and dot voting cover scenarios that a simple “thumbs up” in Slack never will.
The deployment is moderate: five containers, SMTP required, no extreme resource demands. The main limitation is that it’s Rails-based and relatively niche, so the community is smaller than mainstream collaboration tools.
FAQ
Can Loomio replace Discourse or a forum?
No. Loomio is for decision-making, not general discussion. Discussions exist to support proposals and polls, not as standalone forum threads. For general community discussion, use Discourse or Flarum. For decisions, use Loomio.
Is there a mobile app?
No native app, but the web interface is responsive and works well on mobile browsers. Push notifications work through email, not mobile push.
Can I use Loomio for public voting?
Yes. Groups can be set to “open” where anyone can join and participate. However, Loomio is designed for deliberative decision-making (discussion before voting), not anonymous public polling.
How does the proposal/consent model work?
A proposal has four response options: Agree, Abstain, Disagree, Block. “Block” means “I have a fundamental objection.” The proposal passes when the deadline arrives if no one blocks (or based on the group’s configured passing threshold). This consent-based model comes from sociocracy/holacracy governance.
Can I integrate Loomio with Slack or Teams?
Yes. Loomio supports outgoing webhooks to Slack, Mattermost, and Microsoft Teams. Thread activity and decision outcomes can be posted to channels. Users can also vote directly from email notifications.
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments