once-campfire Documentation

Super simple group chat, without a subscription

4,797 stars Ruby Ruby 3 files Β· ~3,645 tokens
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy once-campfire on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name once-campfire -p 80:80 --restart always basecamp/once-campfire:latest

Technical Documentation: basecamp/once-campfire

ℹ️ Provenance: Multi-Tier Ingestion Β· CodeWiki Reference

Overview (README.md)

Campfire

Campfire is a web-based chat application. It supports many of the features you'd
expect, including:

  • Multiple rooms, with access controls
  • Direct messages
  • File attachments with previews
  • Search
  • Notifications (via Web Push)
  • @mentions
  • API, with support for bot integrations

Running your own Campfire instance

Campfire's Docker image contains everything needed for a fully-functional,
single-machine deployment. This includes the web app, background jobs, caching,
file serving, and SSL. You can use our pre-built image at
ghcr.io/basecamp/once-campfire:latest, or build your own from this repo.

Deploying with ONCE

The easiest way to self-host Campfire is with ONCE.
It will guide you through the initial set up and then keep your instance up to date automatically.

If you don't already have once installed, run this on the machine you want to run Campfire on:

sh
curl https://get.once.com | sh

once will launch as soon as the install is finished.

Choose Campfire from the list of applications, follow the instructions, and ONCE will take care of the rest.

If you prefer the command line to the dashboard, you can deploy directly:

sh
once deploy ghcr.io/basecamp/once-campfire --host chat.example.com

Deploying with Docker

If you'd rather run the Docker image yourself, you can read more about that in the self-hosting guide.

[!TIP]
When you start Campfire for the first time, you'll be guided through a wizard to create an admin account.
The email address that you enter for the admin account will be visible on the sign-in page, it's there so
that people have someone to contact if they need help with their account. If that bothers you, put in any
email address you want and create yourself a new admin account.

Authenticated room, message, sidebar and search pages use a bounded 64 MiB cache per worker.
Set CAMPFIRE_RESPONSE_CACHE_MB=0 to disable it. Every request still checks authentication
and room access; commits from any SQLite writer invalidate complete cached HTML and gzip bodies.
Native HTML, JSON and stream fragments have a separate 64 MiB memory limit per worker;
shared rate limits retain their existing store.

Other implementations

Campfire also has implementations in Django, Laravel, Express, Elixir, Go, Rust and C:

HTTP workload (requests/sec) Rails Django Laravel Express Elixir Go Rust C
Room page 4,101 1,507 3,872 42,636 5,350 53,060 106,494 137,524
Messages page 4,115 1,596 3,995 74,362 5,712 54,800 102,697 144,642
Sidebar 4,333 1,873 4,493 94,329 5,949 59,144 120,294 152,002
Search 4,282 1,862 4,172 84,665 5,848 60,509 121,378 149,487
Post a message 330 262 794 2,155 1,278 9,021 8,037 7,530
Backend KLOC 5.5 5.3 4.1 6.8 11.6 23.2 30.7 94.8

KLOC counts backend code plus executable code in templates; excludes plain HTML, frontend, tests, dependencies and generated files.

Shared verification Β· Detailed results.

Measured with 16 concurrent clients on an AMD Ryzen AI MAX+ 395 with 32 GB RAM,
with four hardware cores allocated to each app.

Development

You are welcome - and encouraged - to modify Campfire to your liking.
Please see our development guide for how to get Campfire set up for local development.

Security

See SECURITY.md for how to report a vulnerability and a description of our trust model.

Browser writes use Rails’ Sec-Fetch-Site header-only protection and Origin check.
HTTPS requires browser metadata; plain HTTP retains the missing-header fallback.
Forms contain no CSRF tokens. Existing cookies, bot APIs and signed uploads remain compatible.


Development (`docs/development.md`)

Development

Setting up

First, get everything installed and configured with:

sh
bin/setup

This installs the system packages Campfire needs (SQLite, ffmpeg), the right Ruby version (via mise), and the app's gems; prepares the database; and starts Redis (in a Docker container called campfire-redis, if it isn't already running locally).

If you want to start over at any point, run:

sh
bin/setup --reset

Running the server

Start the development server with:

sh
bin/dev

You'll be able to access the app at http://localhost:3000.

On first run you'll be guided through creating your admin account, and you can sign in with that account from then on.

Note that Campfire needs Redis (for Action Cable, caching, and background jobs), so if you've restarted your machine or stopped the container, docker start campfire-redis will bring it back.

Web Push notifications

Campfire uses VAPID (Voluntary Application Server Identification) keys to send browser push notifications. For notifications to work in development you'll need to generate a key pair and set these environment variables:

  • VAPID_PRIVATE_KEY
  • VAPID_PUBLIC_KEY

You can generate a fresh pair (along with a secret key base, which you can ignore in development) by running:

sh
script/admin/generate-secrets

Running tests

Run the unit tests with:

sh
bin/rails test

And the browser-based system tests with:

sh
bin/rails test:system

Before pushing your changes, you can run the full CI suite locally - style checks, security audits, and all the tests - with a single command:

sh
bin/ci

Contributing

You are welcome - and encouraged - to modify Campfire to your liking.
If you'd like to contribute your changes back, please read our contributing guide first.


Self Hosting (`docs/self-hosting.md`)

Self-hosting Campfire

Campfire's Docker image contains everything needed for a fully-functional, single-machine deployment.
This includes the web app, background jobs, caching, file serving, and SSL.

[!TIP]
The easiest way to self-host Campfire is with ONCE, which handles installation, updates, and backups for you. See the README for details. This guide covers running the Docker image by hand.

We recommend using ghcr.io/basecamp/once-campfire:latest, which always points to the most recent tagged release - the most stable and battle-tested version of Campfire.

We provide a tagged release for every major, minor and patch version of Campfire, so you can also pin your deployment to a specific version if you want to avoid unexpected changes. For example:

bash
# exactly version 1.4.4
ghcr.io/basecamp/once-campfire:1.4.4

# any 1.4.x version
# e.g. 1.4.4 or 1.4.5
# the last number is usually changed for bug fixes
ghcr.io/basecamp/once-campfire:1.4

# any 1.x version
# e.g. 1.4.4 or 1.5.0
# the middle number is usually changed for changes to, or addition of, features
ghcr.io/basecamp/once-campfire:1

If you want to live on the bleeding edge, the main tag tracks the main release branch instead.
It changes with every merged pull request, so it's the newest - but least battle-tested - version of Campfire.

To run it you'll need three things:

  1. a machine that runs Docker
  2. a mounted volume (so that your database and file attachments are kept around between restarts)
  3. some environment variables for configuration

If you'd rather build the image yourself from your own copy of the source, you can do that too:

sh
docker build -t campfire .

Mounting a storage volume

Campfire keeps all of its storage - the database and uploaded file attachments - inside the path /rails/storage.
By default Docker containers don't persist storage between runs, so you'll want to mount a persistent volume into that location.

The simplest way to do this is with the --volume flag with docker run. For example:

sh
docker run --volume campfire:/rails/storage ghcr.io/basecamp/once-campfire:latest

That will create a named volume (called campfire) and mount it into the correct path.
Docker will manage where that volume is actually stored on your server.

You can also specify the data location yourself, mount a network drive, and more.
Check the Docker documentation to find out more about what's available.

Configuring with environment variables

To configure your Campfire installation, you can use environment variables.
At a minimum you'll want to configure your secret key and your SSL domain.

Secrets

Campfire needs a few secret values that are specific to your instance:

  • SECRET_KEY_BASE - the basis for cryptographic features like signed cookies. This should be a long, unguessable random string.
  • VAPID_PRIVATE_KEY/VAPID_PUBLIC_KEY - a key pair used for sending Web Push notifications.

You can generate them by running:

sh
docker run --rm ghcr.io/basecamp/once-campfire:latest script/admin/generate-secrets

It prints a fresh set of values ready to set as environment variables:

text
SECRET_KEY_BASE=...
VAPID_PRIVATE_KEY=...
VAPID_PUBLIC_KEY=...

Keep them safe and reuse the same values across restarts and upgrades - changing them later will invalidate sessions and push notification subscriptions.

SSL

If you want the Campfire container to handle its own SSL (HTTPS) automatically (via Let's Encrypt), you just need to specify the domain name that you're running it on.
You can do that with the TLS_DOMAIN environment variable.

[!NOTE]
If you're using SSL, you'll want to allow traffic on ports 80 and 443.

So if you were running on chat.example.com you could enable SSL like this:

sh
docker run --publish 80:80 --publish 443:443 --env TLS_DOMAIN=chat.example.com ...

If you are terminating SSL in some other proxy in front of Campfire, or aren't using SSL at all (for example, if you want to run it locally on your laptop), then you should set DISABLE_SSL=true instead and just publish port 80:

sh
docker run --publish 80:80 --env DISABLE_SSL=true ...

Error reporting (optional)

To enable error reporting to Sentry in production, supply your DSN in the SENTRY_DSN environment variable.
To disable Sentry initialization entirely, set SKIP_TELEMETRY=true.

Example

Putting it all together, here's a complete docker run invocation:

sh
docker run \
  --name campfire \
  --publish 80:80 --publish 443:443 \
  --restart unless-stopped \
  --volume campfire:/rails/storage \
  --env SECRET_KEY_BASE=$YOUR_SECRET_KEY_BASE \
  --env VAPID_PUBLIC_KEY=$YOUR_PUBLIC_KEY \
  --env VAPID_PRIVATE_KEY=$YOUR_PRIVATE_KEY \
  --env TLS_DOMAIN=chat.example.com \
  ghcr.io/basecamp/once-campfire:latest

And here's an equivalent docker-compose.yml that you could use to run Campfire via docker compose up:

yaml
services:
  web:
    image: ghcr.io/basecamp/once-campfire:latest
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    environment:
      - SECRET_KEY_BASE=abcdefabcdef
      - TLS_DOMAIN=chat.example.com
      - VAPID_PRIVATE_KEY=myvapidprivatekey
      - VAPID_PUBLIC_KEY=myvapidpublickey
    volumes:
      - campfire:/rails/storage

volumes:
  campfire:

First run

When you start Campfire for the first time, you'll be guided through creating an admin account.

[!TIP]
The email address of this admin account will be shown on the login page so that people who forget their password know who to contact for help.
(You can change this email later in the settings.)

Campfire is single-tenant: any rooms designated "public" will be accessible by all users in the system.
To support entirely distinct groups of customers, you would deploy multiple instances of the application.

Upgrading

All of Campfire's state lives in the mounted volume, so upgrading is a matter of pulling a newer image and recreating the container:

sh
docker pull ghcr.io/basecamp/once-campfire:latest

Any pending database migrations run automatically when the container boots.

Backups

To back up your instance, back up the contents of the /rails/storage volume.

Because the SQLite database may be written to at any moment, you shouldn't copy its files directly while Campfire is running.
Instead, first run script/admin/prepare-backup inside the running container to produce a consistent snapshot of the database (it's written to storage/backups/ inside the volume):

sh
docker exec campfire script/admin/prepare-backup

(If you're using Docker Compose, replace docker exec campfire with docker compose exec web)

Then archive the whole storage volume to a file on the host:

sh
docker run --rm \
  --user root \
  --volume campfire:/rails/storage \
  --volume "$PWD":/backup \
  ghcr.io/basecamp/once-campfire:latest \
  tar czf "/backup/campfire-backup.tar.gz" -C /rails storage

This gives you a campfire-backup.tar.gz in your current directory containing the database snapshot and all uploaded files.
Copy it somewhere safe, ideally off the machine.

To restore, extract the archive back into a (stopped) instance's volume, and replace the live database with the snapshot:

sh
docker run --rm \
  --user root \
  --volume campfire:/rails/storage \
  --volume "$PWD":/backup \
  ghcr.io/basecamp/once-campfire:latest \
  bash -c "tar xzf /backup/campfire-backup.tar.gz -C /rails &&
           cp /rails/storage/backups/production.sqlite3 /rails/storage/db/production.sqlite3 &&
           rm -f /rails/storage/db/production.sqlite3-wal /rails/storage/db/production.sqlite3-shm &&
           chown -R rails:rails /rails/storage"

Then start Campfire again.


--- METRICS ---