AGENT VIEW This page as an agent reads it. Fetch it from /docs/self-hosting.md, or send Accept: text/markdown.

Open llms.txt
GET /docs/self-hosting · accept: text/markdown200 text/markdown; charset=utf-8 · vary: Accept
# Self-hosting

Run your own copy of signo on a server with Docker. It takes about five minutes.

You need a server with Docker, a domain for signo (for example `login.example.com`) whose DNS points at that server, ports 80 and 443 open, and an SMTP account to send sign-in emails.

## 1. Get the code and fill in three settings

```
git clone https://codeberg.org/egglabs/signo.git && cd signo
cp .env.example .env     # set SIGNO_DOMAIN, SMTP_URL and MAIL_FROM
```

## 2. Start it

```
docker compose up -d
```

Caddy gets the HTTPS certificate for your domain. Open `https://login.example.com` and sign up, to check that the email arrives.

## 3. Register your site

```
docker compose exec signo signo clients add --name "Example" --redirect-uri https://example.com/api/auth/callback/signo
```

This prints the issuer, a client ID and a client secret. You can also sign in at `https://login.example.com/account/sites` and add the site there.

## 4. Point your site at it

Put those three values where your site has its Google ones. The guides show the exact changes for [Auth.js](https://usesigno.co.uk/docs/authjs.md), [Better Auth](https://usesigno.co.uk/docs/better-auth.md), [Supabase](https://usesigno.co.uk/docs/supabase.md) and [any other OpenID Connect library](https://usesigno.co.uk/docs/openid-connect.md).

## Managing apps

```
docker compose exec signo signo clients list
docker compose exec signo signo clients remove <client id>
```

Changes apply without a restart. Redirect URIs must use https; http is allowed only for localhost.

## Settings

| Variable | Meaning | Default |
|---|---|---|
| `ISSUER` | signo's public https URL (compose sets it from `SIGNO_DOMAIN`) | `http://localhost:4100` |
| `PORT` | port to listen on | `4100` |
| `DATA_DIR` | where the database and keys live | `./data` (`/data` in Docker) |
| `SMTP_URL` | SMTP server for sign-in emails, e.g. `smtps://user:pass@smtp.example.com:465` | unset: links print in the console |
| `MAIL_FROM` | sender of sign-in emails | `signo <no-reply@ISSUER's host>` |
| `TRUST_PROXY` | set behind a reverse proxy (compose sets `1`) | unset |
| `CLIENT_IP_HEADER` | the header your proxy puts the visitor's IP address in, if it doesn't use `X-Forwarded-For` (alwaysdata uses `x-real-ip`); only for a proxy that always overwrites it | unset |
| `OPERATOR` | who runs this copy, shown on the privacy page and in the footer of every page | unset |
| `CONTACT` | email address for privacy and security reports; with it set, signo also serves `/.well-known/security.txt` | unset |

## Say where your copy runs

The trust and privacy pages have a section called "Where this copy runs". Fill it in by writing a short Markdown file at `DATA_DIR/hosting.md`: who hosts the server and where, who sends the sign-in emails, what the host logs, and how long backups are kept. signo reads it at startup and shows it as written. For example:

```
- **Hosting and database:** Example Hosting, Amsterdam, the Netherlands
- **Sign-in emails:** sent through Example Mail
- **Backups:** kept for 14 days
```

## Backups

Back up the `signo-data` volume, or `DATA_DIR` outside Docker. It holds the database and the signing keys; without the keys, everyone is signed out and sites fetch the new public key on their next sign-in.

## Moving to a new domain

Passkeys belong to the domain signo runs on. If you move signo to a new domain, people sign in once with an email link and add a passkey again, and each site must be given the new issuer URL.