title: Run your own Slirc
crumb: Connect / Self-hosting
description: Run Slirc on your own server: what you need, the two containers, the Ergo oper, Caddy, and your first sign-in.
lead: Slirc runs as two containers on a private network behind Caddy. Bun serves the app, and Ergo handles chat. This guide takes you from a fresh Linux server to your first message.

Prefer hosted? We can run Slirc for you at `yourdomain.slirc.org`, from [€29/month](https://slirc.app/#pricing).

The [Elastic License 2.0](https://github.com/bitbonsai/slirc/blob/main/LICENSE) lets you run Slirc for your own group. Hosting it for others requires permission from the author.

## What you need

- A Linux server with [rootless Podman](https://podman.io/docs/installation), [`podman-compose`](https://github.com/containers/podman-compose), [Git](https://git-scm.com/) and [just](https://just.systems/). Slirc runs on a 2 vCPU, 4 GB box with room to spare. The [Ergo](https://ergo.chat/) image `ghcr.io/bitbonsai/slirc-ergo:v2.19.1` ships for ARM64 and x86_64 under one tag; Podman picks the right one.
- A domain. Point `app.yourdomain` at the server. `docs.yourdomain` and the bare domain are optional.
- [Caddy](https://caddyserver.com/docs/install) on the host for TLS.
- A free [Resend](https://resend.com/) account to deliver sign-in links.
- Your own machine with Git and just for deploys. Deploys push a committed tree and reset the server checkout to that exact commit, so the server never holds uncommitted code.

## 1. Clone and set your host

On your machine:

```bash
git clone https://github.com/bitbonsai/slirc
cd slirc
```

Open `justfile` and set the SSH host, checkout path, and config directory at the top to your own values. Every recipe below uses them.

## 2. Config on the server

Slirc keeps secrets out of git. Two files live in the config directory on the server, both mode `0600`:

```bash
ssh yourserver
mkdir -p ~/.config/slirc && chmod 700 ~/.config/slirc
```

`~/.config/slirc/env` is the app's environment:

```bash
APP_HOST=app.yourdomain
OWNER_NICK=yournick
USERS_JSON={"yournick":"you@example.com"}
IRC_PASSWORDS_JSON={"yournick":"a-long-random-password"}
ERGO_OPER_USER=slirc-org
ERGO_OPER_PASS=another-long-random-password
RESEND_API_KEY=re_...
RESEND_FROM=Slirc <slirc@yourdomain>
VAPID_CONTACT=mailto:you@example.com
```

Optional keys: `GIPHY_API_KEY` for [GIPHY search](https://developers.giphy.com/docs/api/), `HELP_API_KEY` and `HELP_MODEL` for the [help bot and natural-language reminders](/docs/bots), `WELCOME_MODEL` for the welcome helper (see below), `SLIRC_HELPERS_ENABLED=false` to run without the `help`, `remind`, `poll`, and `welcome` helpers. Reminder forms such as `in 20m stretch`, `tomorrow 9:00 pick up cake`, `list`, and `cancel 3` still work without a model.

The `welcome` helper sits in `#open-chat`. When someone joins before verifying their email, it posts three short lines addressed to them: what the room is, that `@help` sends a tour by DM once they verify, and three links worth knowing. For the next fifteen minutes it answers up to two follow-ups from the docs, then asks them to verify. Replies use the same `HELP_API_KEY` and `HELP_BASE_URL` as the help bot; `WELCOME_MODEL` picks the model and falls back to `HELP_MODEL`. Without a key the helper skips the model and goes straight to the verify line. Each nick is greeted at most once a day, and the greetings go through Ergo like any message, so IRC clients see them too.

`~/.config/slirc/ergo.yaml` is Ergo's config. Start from the one in the repo:

```bash
scp ergo/ergo.example.yaml yourserver:~/.config/slirc/ergo.yaml
```

Then edit two things in it. Set `network.name` and `server.name` to your domain. Replace the password hash of the `slirc-org` oper with one for the `ERGO_OPER_PASS` you chose:

```bash
podman run --rm -it ghcr.io/bitbonsai/slirc-ergo:v2.19.1 ergo genpasswd
```

Keep `accounts.authentication-enabled: true`, keep the `slirc-org` oper class as it is, and keep `10.0.0.0/8` in `ip-limits.exempted`. Every browser reaches Ergo through the app's address, so per-IP limits would lock everyone out at once.

## 3. Caddy

Copy `caddy/slirc.caddy` to your Caddy sites directory and replace `slirc.org` with your domain and the checkout paths with yours. The app block is the one that matters:

```
app.yourdomain {
	handle_path /webirc {
		reverse_proxy 127.0.0.1:8067
	}
	handle {
		reverse_proxy 127.0.0.1:9010 {
			lb_try_duration 20s
		}
	}
}
```

Port 8067 is Ergo's WebSocket listener, 9010 is the app. Both bind to localhost only. Reload Caddy.

## 4. Bootstrap

Back on your machine, with a clean tree:

```bash
just bootstrap
```

This pushes `main`, resets the server checkout to it, creates the `slirc` network and the three volumes, starts Ergo, then builds and starts the app. The app registers your account with Ergo from `USERS_JSON` and `IRC_PASSWORDS_JSON`, creates `#open-chat` and `#help`, and starts the helpers. Check it:

```bash
just status
just logs app
```

The first line you want in the log is `app listening on :8080`.

## 5. First sign-in

Open `https://app.yourdomain`, type the email from `USERS_JSON`, and open the link Resend delivers. That account is the owner: it can create channels, invite people, and connect agents and bots. Everyone else joins with an email; see [Getting started](/docs/getting-started).

## Day to day

```bash
just deploy          # tests, then rebuild and recreate the app; Ergo keeps running
just deploy-ergo     # recreate Ergo; the app keeps running
just deploy-docs     # publish the docs site, if you serve one
just logs ergo
```

`deploy` refuses to run if Ergo restarted underneath it, and `deploy-ergo` refuses if the app did.

## Backups

Three things hold state:

- `slirc-org-data`, the volume with the app's SQLite database (accounts, sessions, channel metadata, uploads).
- `slirc-org-ergo-data`, the volume with Ergo's history and account database.
- `~/.config/slirc/`, the two config files, which hold every secret.

Copy the config directory with `scp` or your usual file backup. For the volumes, `podman volume export slirc-org-data > app-data.tar` writes a tarball you can move off the box, and `podman volume import` restores it. Ergo writes its database while running, so stop the Ergo container before exporting `slirc-org-ergo-data`, or follow the copy guidance in the [Ergo manual](https://github.com/ergochat/ergo/blob/master/docs/MANUAL.md) for a live copy. The app has no backup recipe yet; a cron line around the two exports is enough.

## When something is off

- **"Reconnecting…" in the browser after a burst of joins.** The `10.0.0.0/8` exemption is missing from `ergo.yaml`. Add it and `POST /v1/rehash` from inside the app container, or run `just deploy-ergo`.
- **`owner credentials are missing` on start.** `OWNER_NICK` is not a key in both `USERS_JSON` and `IRC_PASSWORDS_JSON`.
- **Sign-in links never arrive.** Check `RESEND_FROM` is a verified sender on your Resend account, then `just logs app` for the send error.
- **Ergo logs `read ircd.yaml: is a directory`.** The `ergo.yaml` file was missing when the container started, and the bind mount created a directory in its place. Remove that directory, restore the file, recreate Ergo.
