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.

FeatureLoomioTypical Forum
Threaded discussionsYesYes
Structured voting/polls7 voting types built inNo (or basic polls)
Decision outcomes trackedYes — with deadlines and resultsNo
Proposals with accept/decline/abstainYesNo
Email integrationReply by emailNotifications only
SubgroupsYes — nested groups with permissionsSubcategories
Real-time collaborationHocuspocus (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)
RequirementMinimumRecommended
CPU2 cores4 cores
RAM2 GB4 GB
Disk10 GB20 GB+
EmailRequired (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:

ToolUse CaseHow It Works
ProposalBinary decisionAgree, Abstain, Disagree, Block
PollMultiple choiceChoose one or more options
Score PollRate optionsScore each option 0-10
Ranked ChoicePriority orderRank options by preference
Time PollSchedule meetingSelect available time slots
Dot VoteBudget allocationDistribute limited dots across options
CheckAcknowledgmentSimple 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:

DataLocationPriority
PostgreSQL databasepgdata volumeCritical — all groups, threads, decisions
Uploaded filesuploads + files volumesImportant — attachments, avatars
Environment file.envCritical — 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

ComponentRAM (idle)RAM (active)CPUDisk
App (Rails/Puma)~300 MB~500 MBLow-MediumMinimal
Worker (Sidekiq)~200 MB~400 MBMediumMinimal
Hocuspocus~80 MB~150 MBLowMinimal
PostgreSQL~100 MB~300 MBLow-MediumGrows with content
Redis~30 MB~80 MBMinimalMinimal
Total~710 MB~1.4 GBLow-Medium10 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.

Comments