Campfire
Overview
This guide covers the deployment and configuration of Campfire on Ubuntu 24.04 on Azure using SecureAnyCloud Azure Marketplace images. Campfire is a web-based chat application for teams. It offers multiple rooms with access controls, so a conversation can be open to everyone or closed to a few. It also provides one-to-one direct messages, file attachments with inline previews, @mentions that notify the right person, and full-text search across the whole history. Web Push notifications reach people who have the tab closed, and a simple bot API lets scripts and services post into a room.
The SecureAnyCloud image ships the free and open source, MIT licensed Campfire from the official upstream container image. The image is pinned by both tag and digest, so the deployment never silently moves to a different build. Campfire is a single-machine product. One instance holds the whole team's conversation, and all of it, including every uploaded file, stays inside your own Azure subscription.
The image ships with no known credential. A fresh Campfire database leaves the first-run wizard open to whoever reaches the machine first, so on first boot this image creates the single administrator itself, with a random per-instance password. That step closes the wizard before the instance is reachable. Every VM also generates its own application secret and Web Push key pair.
Campfire, ONCE and 37signals are trademarks of 37signals, LLC. SecureAnyCloud produces this image, and it is not affiliated with, endorsed by or sponsored by 37signals, LLC. The image ships the free and open source MIT licensed self-hosted software, unmodified.
What you get:
- Campfire 1.4.5 (MIT licensed, from 37signals), deployed from the official upstream container image and pinned by tag and digest
- A complete single-machine stack inside that image: Thruster in front of Puma running Rails 8 on Ruby 3.4.5, a bundled Redis for live updates and caching, and background job workers
- The web app published on port
80, with all state (the SQLite database and every uploaded attachment) held in one Docker volume campfire.serviceandcampfire-firstboot.serviceas systemd units, enabled and active on boot- A unique application secret, a unique Web Push key pair and a single administrator account with a random password, all generated per VM on first boot and never baked into the image
- No default login, no shipped secret and no open first-run wizard for a passer-by to claim
- An Ubuntu 24.04 LTS base with the latest security patches applied at build time
- The Azure Linux Agent for cloud integration and SSH key injection
- 24/7 SecureAnyCloud support with a guaranteed 24 hour response SLA
Prerequisites
- An active Azure subscription, an SSH public key, and a VNet and subnet in the target region
- A subscription to the Campfire listing on Azure Marketplace
Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) is a sensible starting point for a small team. For a larger team or heavy attachment traffic, use Standard_B2ms or Standard_D2s_v5.
In the NSG, allow inbound 22/tcp from your management network and 80/tcp for the web app from the networks that use it.
Deploy from the Azure Portal
Sign in to the Azure portal, choose Create a resource, search the Marketplace for Campfire by SecureAnyCloud, and select Create. On the Basics tab, pick your subscription, resource group, region and size. Under Administrator account, choose SSH public key and paste your key. Under Inbound port rules, allow SSH (22) and HTTP (80). Then choose Review + create, and then Create.
Deploy from the Azure CLI
RG="campfire-prod"; LOCATION="eastus"; VM_NAME="campfire-01" IMAGE_URN="sac1647283583153:campfire-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION" az network vnet create -g "$RG" --name cf-vnet --address-prefix 10.100.0.0/16 --subnet-name cf-subnet --subnet-prefix 10.100.1.0/24 az network nsg create -g "$RG" --name cf-nsg az network nsg rule create -g "$RG" --nsg-name cf-nsg --name allow-ssh --priority 100 \
--source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp az network nsg rule create -g "$RG" --nsg-name cf-nsg --name allow-http --priority 110 \
--destination-port-ranges 80 --access Allow --protocol Tcp
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:campfire-ubuntu-24-04:default:latest
az vm create -g "$RG" --name "$VM_NAME" --image "$IMAGE_URN" \
--size Standard_B2s --storage-sku StandardSSD_LRS \
--admin-username azureuser --ssh-key-values "$SSH_KEY" \
--vnet-name cf-vnet --subnet cf-subnet --nsg cf-nsg --public-ip-sku StandardConnect to your VM
ssh azureuser@<vm-ip>Confirm the services are running
Campfire runs as one container under campfire.service. That single container is a complete deployment. Thruster fronts Puma running the Rails application, a bundled Redis carries live updates and the cache, and background workers deliver notifications. Only port 80 is published. Confirm that the services are active and list the container:
sudo systemctl is-active docker campfire-firstboot campfire
sudo docker compose -f /var/lib/campfire/docker-compose.yml psAll three services report active and the campfire container reports Up, with 0.0.0.0:80->80/tcp published. campfire-firstboot.service is a one-shot unit that has already finished. It stays active because it remains after exit, and it does not run again on this VM.
Read the per-instance credentials
On this VM's first boot, before the application was reachable, the image generated a unique application secret and Web Push key pair. It also created a single administrator account with a random password and wrote the password to a root-only file. Read the file with:
sudo cat /root/campfire-credentials.txt
sudo stat -c '%a %U:%G %n' /root/campfire-credentials.txtThe file (mode 0600 root:root) holds CAMPFIRE_ADMIN_EMAIL and CAMPFIRE_ADMIN_PASSWORD, the account you sign in to the web app with, plus the app url. None of these values ship in the image. Every value is unique to this VM, so no two instances share a credential and there is no default login to change.
Verify the security model
Campfire's health endpoint is open by design, so a load balancer can probe it. Every other route requires a signed-in session. The first-run wizard that would let a visitor claim the instance is already closed, because first boot created the administrator. Confirm all four behaviors:
B=http://localhost curl -s -o /dev/null -w 'GET /up (no auth): %{http_code}\n' $B/up curl -s -o /dev/null -w 'GET /first_run (no auth): %{http_code}\n' $B/first_run curl -s -o /dev/null -w 'GET /rooms/1 (no auth): %{http_code}\n' $B/rooms/1 curl -s -o /dev/null -w 'POST /rooms/1/messages (no auth): %{http_code}\n' -X POST $B/rooms/1/messages/up returns 200. /first_run returns 302. The wizard redirects away because an account already exists, so nobody can claim this instance. Reading a room and posting into a room both return 302 to the sign-in page for an anonymous visitor. That is the security model in four lines: one open health probe, and everything else behind a session.
Sign in from the command line
Prove that the per-instance credential authenticates before you open a browser. Campfire uses Rails' per-form CSRF tokens, so take the session-wide token from the page's csrf-token meta tag:
B=http://localhost
J=$(mktemp)
TOK=$(curl -s -c $J -b $J $B/session/new | grep -oE 'csrf-token" content="[^"]+"' | head -1 | sed -E 's/.*content="([^"]+)".*/\1/')
curl -s -c $J -b $J -o /dev/null -w 'sign in (per VM admin): %{http_code}\n' \
--data-urlencode "authenticity_token=$TOK" \
--data-urlencode "email_address=<CAMPFIRE_ADMIN_EMAIL>" \
--data-urlencode "password=<CAMPFIRE_ADMIN_PASSWORD>" \
-X POST $B/session
curl -s -b $J -c $J -o /dev/null -w 'landing page (session): %{http_code}\n' $B/ rm -f $JThe sign-in returns 302, because Campfire redirects you into your last room. The landing page then returns 302 as well, redirecting to the room you belong to. A wrong password returns 401 instead.
Open the web app and sign in
Browse to http://<vm-ip>/ to reach Campfire. Sign in with the email address and password from Step 5. By upstream design, the sign-in page shows the email address, so that anyone who forgets their password knows who to ask.
Talk in a room
You land in the first room, called All Talk, which every member of the account joins automatically. Type in the composer at the bottom and press Enter. Messages appear live for everyone in the room without a refresh. The paperclip button attaches a file, and the A button turns on rich text.
The right-hand rail lists everyone in the account at the top and every room below it. The + button creates a new room.
Invite your team and manage the account
Open the settings gear in the bottom right corner to reach Account settings. Here you can rename the account, upload a logo, decide whether ordinary members may create rooms, and copy the invite link.
Anyone who opens the invite link creates their own account on this instance. Treat the link like a password, and regenerate it with the refresh button if it leaks. The member list below lets you promote someone to administrator with the crown button, or remove them.
Search the whole history
Click the magnifying glass to the left of the composer and type your search. Campfire searches every message that you can see across every room. Each result shows the author, the time and the room it came from, so you can jump straight to it.
Confirm which build you are running
The image reference is pinned by tag and by digest, so an upgrade happens only when you choose it. Confirm what is running:
sudo docker compose -f /var/lib/campfire/docker-compose.yml images
sudo docker image inspect --format '{{index .RepoDigests 0}}' \
"$(sudo docker compose -f /var/lib/campfire/docker-compose.yml images -q | head -1)"The output shows both the tag and the sha256: digest. To move to a newer upstream release, edit the image: line in /var/lib/campfire/docker-compose.yml to the tag and digest you want, then restart the service. Campfire runs any pending database migrations automatically when the container starts.
Serve HTTPS with your own domain
The image ships plain HTTP on port 80, so the VM is reachable on its IP address as soon as it boots. Campfire can obtain and renew its own Let's Encrypt certificate instead. Point a DNS name at the VM and open port 443 in the network security group. Then edit /var/lib/campfire/.env: remove the DISABLE_SSL line, add your domain, publish 443 in the compose file, and restart.
# /var/lib/campfire/.env
# remove this line: DISABLE_SSL=true
# and add: TLS_DOMAIN=chat.example.com
# /var/lib/campfire/docker-compose.yml — publish 443 alongside 80: ports: - "80:80" - "443:443"
# then:
sudo systemctl restart campfire.serviceLeave DISABLE_SSL=true in place if you terminate TLS in front of the VM, for example with an Azure Application Gateway or your own reverse proxy. If you remove it without a resolvable TLS_DOMAIN, the app redirects every plain HTTP request to HTTPS and stops answering on its IP address.
Back up your conversation
Everything Campfire keeps lives in one Docker volume: the SQLite database and every uploaded file. Because the database can be written to at any moment, take a consistent snapshot first instead of copying the file directly:
sudo docker exec campfire script/admin/prepare-backup
sudo docker exec campfire ls -la storage/backupsThat command writes a consistent snapshot into storage/backups inside the volume. Archive the whole volume to a file on the host, and copy the archive somewhere safe, ideally off the machine, on a schedule that suits you. Azure disk snapshots of the OS disk complement this approach, but take the database snapshot first so that the copy is consistent.
Troubleshooting
The web app does not answer. Check the container and the last log lines with:
sudo systemctl status campfire.service --no-pager | head -20 sudo docker compose -f /var/lib/campfire/docker-compose.yml logs --tail 40 campfireYou lost the administrator password. Reset the password from inside the container. Replace the address and the new password with your own:
sudo docker exec campfire script/admin/reset-password admin@example.com 'a-new-strong-password'Everything redirects to HTTPS and the site does not load on the IP address. DISABLE_SSL was removed from /var/lib/campfire/.env without a working TLS_DOMAIN. Put the line back and restart the service.
Support
SecureAnyCloud provides 24/7 support with a guaranteed 24 hour response SLA for this image. Contact us at sales@secureanycloud.com or visit secureanycloud.com.
Deploy on Azure
Launch Campfire on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com