Quickest path. The setup script handles configuration, keypair generation, and starting all services. You just need Docker and a domain pointing at your server.
Prerequisites
Server
A VPS or dedicated server running Linux. 1 GB RAM minimum, 2 GB recommended.
Domain
A domain or subdomain with DNS pointing at your server. Required for TLS and federation.
Docker
Docker Engine with the Compose plugin (v2). Install via docs.docker.com.
Reverse Proxy
nginx or Caddy on the host for TLS termination. The containers only listen on port 80.
Janus WebRTC
Required for voice and video channels. Run your own instance or leave JANUS_URL unset to disable voice/video.
1. Get the Code
The public repository is temporarily unavailable. This guide will be updated with installation instructions when the source is re-released.
2. Run the Setup Script
The setup script walks you through configuration, generates your node keypair, writes a .env file, and starts everything in Docker.
chmod +x setup.sh
./setup.sh
You will be prompted for:
- Node URL — the public HTTPS URL of your node (e.g.
https://chat.example.com). This becomes part of every user's global ID and cannot be changed once you have federated with other nodes.
- Node display name — a friendly name shown in the node directory.
- Invite code — optional. Restricts signups to people who know the code. Leave blank for open registration.
- Seed node URL — optional. The URL of an existing flebop node to federate with on first start. Leave blank to run standalone.
The script generates an ECDSA P-256 keypair and saves it to ./node.key (chmod 600). It is mounted read-only into the containers. Keep this file safe — it authenticates your node to the rest of the federation.
Keep node.key secret. Never commit it to git or copy it to a world-readable location. If it is compromised, regenerate it with docker compose exec gunicorn python manage.py generate_node_keypair --force --private-key-path /app/node.key and re-register with all federated nodes.
Voice and video require Janus. After setup, add JANUS_URL=wss://your-janus-host/janus to your .env and restart (docker compose restart gunicorn daphne). Without it, text channels and federation work fine — only voice/video channels are unavailable. See janus.conf.meetecho.com for setup instructions.
3. Set Up TLS
The containers listen on port 80. Your host reverse proxy handles TLS and forwards traffic to it.
Option A — Caddy (simplest)
Caddy handles certificate issuance automatically.
sudo apt install caddy
chat.example.com {
reverse_proxy localhost:80
}
sudo systemctl reload caddy
Option B — nginx + Certbot
sudo apt install nginx certbot python3-certbot-nginx
server {
listen 80;
server_name chat.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name chat.example.com;
ssl_certificate /etc/letsencrypt/live/chat.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/chat.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:80;
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;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
}
}
sudo ln -s /etc/nginx/sites-available/flebop /etc/nginx/sites-enabled/
sudo certbot --nginx -d chat.example.com
sudo systemctl reload nginx
4. Create an Admin Account
docker compose exec gunicorn python manage.py createsuperuser
5. Verify
- Visit
https://chat.example.com — the login page loads over HTTPS.
- Log in to the admin panel at
/admin/.
- Send a message in a channel. If it appears without a page refresh, WebSockets are working.
- Check your well-known endpoint:
curl https://chat.example.com/.well-known/flebop-node should return JSON with a public_key.
Joining the Federation
If you didn't enter a seed node during setup, you can join the federation at any time:
docker compose exec gunicorn python manage.py register_node https://other.example.com
docker compose exec gunicorn python manage.py ping_nodes
New registrations are inactive by default on the receiving node. The receiving node admin must approve them before cross-node traffic is accepted:
# List pending nodes
docker compose exec gunicorn python manage.py approve_node
# Approve a specific node
docker compose exec gunicorn python manage.py approve_node https://other.example.com
Updating
git pull
docker compose up --build -d
Migrations and static file collection run automatically on container start via the entrypoint.
Useful Commands
docker compose logs -f
docker compose exec gunicorn python manage.py shell
docker compose down
docker compose down -v
More control. Run flebop directly on the host using a Python virtualenv, systemd services, and your own Redis and nginx setup.
Prerequisites
Server
A VPS or dedicated server running Linux. 1 GB RAM minimum, 2 GB recommended.
Domain
A domain or subdomain pointing at your server. TLS is required for federation.
Python 3.11+
Python 3.11 or newer. 3.12 recommended.
Redis
Redis 6+. Used for WebSocket channel layers and real-time messaging.
Nginx
Reverse proxy for TLS termination and static file serving.
Certbot
For obtaining a Let's Encrypt TLS certificate.
Janus WebRTC
Required for voice and video channels. Set JANUS_URL in .env to point at your instance.
1. Get the Code
The public repository is temporarily unavailable. This guide will be updated with installation instructions when the source is re-released.
cd /srv/flebop
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
2. Configure the Environment
python -c "import secrets; print(secrets.token_urlsafe(50))"
Create a .env file in the project root:
DJANGO_SECRET_KEY=your-generated-secret-key
DEBUG=False
ALLOWED_HOSTS=chat.example.com
CSRF_TRUSTED_ORIGINS=https://chat.example.com
REDIS_HOST=127.0.0.1
NODE_URL=https://chat.example.com
NODE_NAME=My Node
NODE_PRIVATE_KEY_PATH=/home/user/flebop/node.key
JANUS_URL=wss://janus.example.com/janus
NODE_URL must be the full public HTTPS URL of your node. It becomes part of every user's global ID and cannot be changed once you have registered with other nodes.
3. Run Migrations
python manage.py migrate
4. Generate the Node Keypair
Each flebop node authenticates with other nodes using an ECDSA P-256 keypair. The private key stays on your server and is never transmitted.
python manage.py generate_node_keypair --private-key-path ~/flebop/node.key
ls -la ~/flebop/node.key
Keep this file secret. Never commit it to git or copy it to a world-readable location. If it is compromised, regenerate it and re-register with all federated nodes. Add *.key to your .gitignore.
Set NODE_PRIVATE_KEY_PATH in your .env to the full path, then verify:
python manage.py init_local_node
5. Collect Static Files
python manage.py collectstatic --no-input
6. Create an Admin Account
python manage.py createsuperuser
7. Set Up Redis
sudo apt install redis-server
sudo systemctl enable --now redis-server
redis-cli ping
8. Set Up Gunicorn & Daphne
flebop uses two processes: Gunicorn for HTTP and Daphne for WebSockets. Create a systemd service for each.
Gunicorn
sudo nano /etc/systemd/system/flebop-gunicorn.service
[Unit]
Description=flebop Gunicorn
After=network.target
[Service]
User=your-user
WorkingDirectory=/srv/flebop
EnvironmentFile=/srv/flebop/.env
ExecStart=/srv/flebop/venv/bin/gunicorn \
--workers 3 \
--bind 127.0.0.1:8000 \
voip_project.wsgi:application
Restart=on-failure
[Install]
WantedBy=multi-user.target
Daphne (WebSockets)
sudo nano /etc/systemd/system/flebop-daphne.service
[Unit]
Description=flebop Daphne
After=network.target redis.service
[Service]
User=your-user
WorkingDirectory=/srv/flebop
EnvironmentFile=/srv/flebop/.env
ExecStart=/srv/flebop/venv/bin/daphne \
-b 127.0.0.1 \
-p 8001 \
voip_project.asgi:application
Restart=on-failure
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now flebop-gunicorn flebop-daphne
9. Configure Nginx
sudo nano /etc/nginx/sites-available/flebop
server {
listen 80;
server_name chat.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name chat.example.com;
ssl_certificate /etc/letsencrypt/live/chat.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/chat.example.com/privkey.pem;
client_max_body_size 10M;
location /static/ {
alias /srv/flebop/staticfiles/;
expires 30d;
add_header Cache-Control "public, immutable";
}
location /media/ {
alias /srv/flebop/media/;
}
location /ws/ {
proxy_pass http://127.0.0.1:8001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
proxy_read_timeout 86400;
}
location / {
proxy_pass http://127.0.0.1:8000;
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;
proxy_read_timeout 60;
}
}
sudo ln -s /etc/nginx/sites-available/flebop /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
10. TLS Certificate
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d chat.example.com
11. Verify
- Visit
https://chat.example.com — the login page loads over HTTPS.
- Log in to the admin panel at
/admin/.
- Send a message in a channel — if it appears without a page refresh, WebSockets are working.
- Check the well-known endpoint:
curl https://chat.example.com/.well-known/flebop-node should return JSON with a public_key.
Joining the Federation
python manage.py register_node https://other.example.com
python manage.py ping_nodes
New registrations are inactive by default on the receiving node. The receiving node admin must approve them before cross-node traffic is accepted:
# List pending nodes
python manage.py approve_node
# Approve a specific node
python manage.py approve_node https://other.example.com
Updating
cd /srv/flebop
git pull
source venv/bin/activate
pip install -r requirements.txt
python manage.py migrate
python manage.py collectstatic --no-input
sudo systemctl restart flebop-gunicorn flebop-daphne