A community calendar
you actually own

Gather is a self-hosted calendar app for local communities. Collect, moderate, and share events — with federation, feeds, and no vendor lock-in.

See it live: perthshire.events — a community calendar for Perthshire, Scotland, running on Gather.

Everything your community needs

📅

Event submission

Let your community submit events with optional moderation before publishing.

🛡️

Moderation workflow

Role-based access control — user, editor, and admin roles with fine-grained permissions.

🦣

ActivityPub federation

Follow your calendar from Mastodon or any ActivityPub-compatible app.

📡

RSS & iCal feeds

Subscribe to events in any feed reader or calendar app.

🗺️

Location support

Location-based events using OpenStreetMap data — no Google Maps required.

🔁

Recurring events

Schedule weekly, monthly, and custom recurring events with ease.

📝

Custom pages

Create About, FAQ, and Code of Conduct pages with full Markdown support.

🎨

Custom branding

Set your site name, favicon, SEO description, and custom CSS.

🏷️

Tags & location filters

Filter events by tag or town. Visitors find what's relevant without scrolling.

🌗

Dark / light theme

Automatic theme switching based on system preference, with a manual toggle.

📦

Single binary

One binary, no external database, no external dependencies. Runs anywhere.

Up and running in minutes

1

Create a docker-compose.yml

The quickest way to run Gather is with Docker Compose. Create the file below on your server:

services:
  gather:
    image: ghcr.io/grantstephens/gather:latest
    container_name: gather
    restart: unless-stopped
    ports:
      - "8090:8090"
    volumes:
      - gather_data:/app/pb_data
    environment:
      - PB_ADMIN_EMAIL=admin@example.com
      - PB_ADMIN_PASSWORD=changeme
      - BASE_URL=https://your-domain.com

volumes:
  gather_data:
Change the defaults: Set a real email, a strong password, and your actual domain for BASE_URL.
2

Start the container

docker compose up -d

Gather will be running at http://localhost:8090.

3

Put it behind a reverse proxy

For HTTPS in production, proxy port 8090 with Caddy, nginx, or Traefik.

Caddy (simplest — handles HTTPS automatically):

your-domain.com {
    reverse_proxy localhost:8090
}

nginx:

server {
    listen 443 ssl http2;
    server_name your-domain.com;

    location / {
        proxy_pass http://localhost:8090;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
4

Create your admin account

Gather uses two separate account systems (a PocketBase limitation):

  • PocketBase superuser — manages the database at /_/, created automatically from your env vars.
  • App user — logs into the frontend at /login. Register separately, then promote to admin via /_/ > Collections > users > set role to admin.

Once you have a frontend admin account, go to Admin > Settings to configure:

  • Site name and subtitle
  • Favicon (auto-converted to WebP)
  • SEO description for search engines and social previews
  • Moderation settings (anonymous submissions, require approval)
  • ActivityPub federation toggle
  • Custom CSS and tracking/head code

Alternative: Docker Run

docker run -d \
  --name gather \
  -p 8090:8090 \
  -v gather-data:/app/pb_data \
  -e PB_ADMIN_EMAIL=admin@example.com \
  -e PB_ADMIN_PASSWORD=changeme \
  -e BASE_URL=https://your-domain.com \
  ghcr.io/grantstephens/gather:latest

Alternative: Build from Source

Requires Go 1.25+, Node.js 18+, and libwebp-dev.

git clone https://github.com/grantstephens/gather.git
cd gather
make build
./gather serve

Environment variables

Variable Default Description
PB_ADMIN_EMAIL admin@example.com Superuser email for the PocketBase dashboard
PB_ADMIN_PASSWORD changeme Superuser password — change this!
BASE_URL http://localhost:8090 Public URL (used for ActivityPub and feed links)
PB_ENCRYPTION_KEY empty Encryption key for sensitive data (openssl rand -hex 32)
Feeds: Your instance automatically exposes RSS at /feed/events.rss, iCal at /ical/events.ics, and (when enabled) an ActivityPub actor at /ap/actor.

Custom Pages

Create static pages (About, FAQ, Code of Conduct, etc.) from the Admin > Pages tab. Pages can appear in the navigation bar, footer, or both, and support Markdown content.

PocketBase Admin

The PocketBase admin dashboard is available at /_/ for direct database access, collection management, and log viewing.

Updates & backups

Updating to the latest version

docker compose pull
docker compose down
docker compose up -d

Backing up your data

All data lives in the pb_data Docker volume. Back it up with:

docker run --rm \
  -v gather_gather_data:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/gather-backup-$(date +%Y%m%d).tar.gz -C /data .