ANetBBS on Raspberry Pi — Full Install Guide
ANetBBS runs well on Raspberry Pi 3 and later. This guide covers everything
from hardware selection through a running BBS reachable from the internet.
Hardware Recommendations
Minimum
- Raspberry Pi 3B/3B+ with 1 GB RAM — BBS runs great; in-browser DOS games (Doom, Duke Nukem) not recommended (browser emulation is CPU-heavy)
- A2-rated microSD card (SanDisk Extreme Pro 32 GB+ or Samsung Pro Endurance)
Recommended
- Raspberry Pi 4 with 4 GB RAM or better
- Raspberry Pi 5 with 8 GB RAM (best performance)
- USB SSD for the install dir (see Moving data/ to a USB SSD)
- A2-rated microSD for the OS; USB SSD for data
Why SSD matters
SQLite does frequent small random writes. A good A2 SD card handles it fine for
a small BBS, but a USB SSD (Samsung T7, WD My Passport SSD) eliminates write
wear and latency spikes under load. The OS can stay on the SD card; only the
ANetBBS install dir needs to be on SSD.
OS Recommendations
Use a 64-bit OS. ANetBBS requires Python 3.10+ and 64-bit is needed for
full ARM64 performance and compatibility.
| OS | Notes |
|---|---|
| Raspberry Pi OS Lite 64-bit (Bookworm) | Best supported. Python 3.11. Lean, no desktop. |
| Ubuntu 24.04 Server for Pi | Python 3.12. Same environment as bbs.a-net.fyi. |
| Raspberry Pi OS Full 64-bit | Works, but the desktop wastes RAM. |
Flash the OS
Use Raspberry Pi Imager (https://www.raspberrypi.com/software/) to flash
the image. In the Imager settings (gear icon), set:
- Hostname (e.g. noverdu)
- Username + password
- Wi-Fi or leave blank for Ethernet
- Enable SSH
Pre-Install System Setup
SSH into your Pi and run these before install.sh:
sudo apt update && sudo apt upgrade -y
# Required system packages
sudo apt install -y python3 python3-pip python3-venv \
git curl wget nginx certbot python3-certbot-nginx \
sqlite3 openssl ufw telnet
# Optional but useful
sudo apt install -y htop tmux fail2ban
Enable ufw
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 2233/tcp # telnet
sudo ufw allow 2234/tcp # SSH (BBS)
sudo ufw enable
Download ANetBBS
cd ~
wget https://github.com/anetonline/ANetBBS/releases/latest/download/ANetBBS-v1.0a2.NNN.tar.gz
# or check /downloads/ on bbs.a-net.fyi for the latest tarball
tar xzf ANetBBS-v1.0a2.NNN.tar.gz
cd ANetBBS-v1.0a2.NNN
Run the Installer
sudo bash install.sh
Pi-specific answers for the installer prompts
| Prompt | Pi recommendation |
|---|---|
| Mode | test if behind NAT without a domain; production if you have a domain + certbot |
| Install directory | /home/<your-username>/anetbbs (e.g. /home/pi/anetbbs) |
| System service user | Your username (e.g. pi) — avoids permission headaches |
| Web port | 5000 (or 80 if not using nginx) |
| Enable SSL | Only if you have a real domain pointed at this Pi |
| Telnet port | 2233 |
| SSH port | 2234 |
Install to your home directory, not
/opt/anetbbs.
/optis owned by root and can cause permission edge cases on Raspbian.
Using/home/<user>/anetbbsmeans the service user already owns the whole tree.
DDNS Setup (for Pis Behind a Home Router)
Most Pis are behind NAT — your router has the public IP, not the Pi. You need:
- A Dynamic DNS (DDNS) hostname that follows your public IP
- Port forwarding on your router
Free DDNS services
| Service | URL | Notes |
|---|---|---|
| DuckDNS | https://www.duckdns.org | Easiest, free, good updater |
| No-IP | https://www.noip.com | Free tier, must confirm monthly |
| FreeDNS | https://freedns.afraid.org | Many TLDs available |
| Dynu | https://www.dynu.com | Free, no nag emails |
DuckDNS auto-update (recommended)
mkdir ~/duckdns && cd ~/duckdns
# Replace TOKEN and SUBDOMAIN with yours from duckdns.org
cat > duck.sh << 'EOF'
echo url="https://www.duckdns.org/update?domains=YOURSUBDOMAIN&token=YOURTOKEN&ip=" \
| curl -k -o ~/duckdns/duck.log -K -
EOF
chmod +x duck.sh
# Run every 5 minutes
(crontab -l 2>/dev/null; echo "*/5 * * * * ~/duckdns/duck.sh >/dev/null 2>&1") | crontab -
Router port forwarding
Log into your router (usually 192.168.1.1 or 192.168.0.1) and forward:
| External port | Internal IP | Internal port | Protocol |
|---|---|---|---|
| 80 | Pi's IP | 80 | TCP |
| 443 | Pi's IP | 443 | TCP |
| 2233 | Pi's IP | 2233 | TCP |
| 2234 | Pi's IP | 2234 | TCP |
Find your Pi's local IP:
hostname -I
Set a static local IP on your Pi so the forwarding doesn't break:
# /etc/dhcpcd.conf (Raspbian) or netplan (Ubuntu)
# Raspbian: add to /etc/dhcpcd.conf
echo "
interface eth0
static ip_address=192.168.1.XXX/24
static routers=192.168.1.1
static domain_name_servers=1.1.1.1 8.8.8.8
" | sudo tee -a /etc/dhcpcd.conf
sudo systemctl restart dhcpcd
SSL with Let's Encrypt (if you have a real domain)
Once your DDNS hostname resolves to your public IP and port 80 is forwarded:
sudo certbot --nginx -d yourname.duckdns.org
Certbot auto-renews via a systemd timer. Verify:
sudo certbot renew --dry-run
Then in your ANetBBS .env:
SESSION_COOKIE_SECURE=true
SESSION_COOKIE_SECURE — Important for HTTP-only setups
If you are not using nginx+SSL (running directly on port 5000 over plain HTTP):
SESSION_COOKIE_SECUREmust befalse(the default since v1.0a2.72)- Check your
.env:
grep SESSION_COOKIE_SECURE ~/anetbbs/.env
If the line is missing or set to true, fix it:
echo "SESSION_COOKIE_SECURE=false" | sudo tee -a ~/anetbbs/.env
sudo systemctl restart anetbbs-web
Setting it to true on a plain-HTTP site causes the browser to silently drop
the session cookie, which makes every login attempt fail with a CSRF error.
Moving data/ to a USB SSD
The data/ directory holds your database, uploads, and user files — the parts
that get written most. Moving it to an SSD extends SD card life and improves
performance.
1. Prepare the SSD
# List disks — your SSD is probably /dev/sda
lsblk
# Partition and format (WARNING: destroys existing data on /dev/sda)
sudo fdisk /dev/sda
# Press: n → p → 1 → Enter → Enter → w
sudo mkfs.ext4 /dev/sda1
2. Mount the SSD
sudo mkdir -p /mnt/bbsdata
# Get the UUID
sudo blkid /dev/sda1
# Add to /etc/fstab (replace UUID with yours)
echo "UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /mnt/bbsdata ext4 defaults,noatime 0 2" \
| sudo tee -a /etc/fstab
sudo mount -a
sudo chown $(whoami):$(whoami) /mnt/bbsdata
3. Move the data directory
Stop the BBS first:
sudo systemctl stop anetbbs-web anetbbs anetbbs-mrc-bridge 2>/dev/null; true
Copy and swap:
INSTALL_DIR=~/anetbbs # adjust if different
cp -a "$INSTALL_DIR/data" /mnt/bbsdata/
mv "$INSTALL_DIR/data" "$INSTALL_DIR/data.bak"
ln -s /mnt/bbsdata/data "$INSTALL_DIR/data"
Verify the symlink:
ls -la "$INSTALL_DIR/data"
# should show: data -> /mnt/bbsdata/data
4. Update DATA_DIR and DATABASE_URL in .env
cat >> "$INSTALL_DIR/.env" << EOF
DATA_DIR=/mnt/bbsdata/data
DATABASE_URL=sqlite:////mnt/bbsdata/data/anetbbs.db
EOF
5. Restart and verify
sudo systemctl start anetbbs-web anetbbs
sudo systemctl status anetbbs-web anetbbs
Log in and confirm everything works, then remove the backup:
rm -rf "$INSTALL_DIR/data.bak"
Updating ANetBBS on Pi
cd ~
wget https://github.com/anetonline/ANetBBS/releases/latest/download/ANetBBS-v1.0a2.NNN.tar.gz
tar xzf ANetBBS-v1.0a2.NNN.tar.gz
sudo bash ANetBBS-v1.0a2.NNN/update.sh
update.sh auto-detects the running install, backs up your data, syncs the
new code, runs DB migrations, and restarts services. Your .env, database,
uploads, and user files are preserved.
Service Management
# Status of all ANetBBS services
sudo systemctl status anetbbs-web anetbbs
# Restart everything
sudo systemctl restart anetbbs-web anetbbs
# View live logs
sudo journalctl -u anetbbs-web -f
sudo journalctl -u anetbbs -f
# View last 100 lines
sudo journalctl -u anetbbs-web -n 100
sudo journalctl -u anetbbs -n 100
Troubleshooting
"unable to open database file" on telnet/SSH
Fixed in v1.0a2.74. If on an older version, add DATABASE_URL to .env:
INSTALL_DIR=~/anetbbs # adjust if different
echo "DATABASE_URL=sqlite:///${INSTALL_DIR}/data/anetbbs.db" | sudo tee -a "$INSTALL_DIR/.env"
sudo systemctl restart anetbbs
"The CSRF session token is missing" on web login
Your site is running over plain HTTP but SESSION_COOKIE_SECURE=true. Fixed by
default in v1.0a2.72. On older versions:
echo "SESSION_COOKIE_SECURE=false" | sudo tee -a ~/anetbbs/.env
sudo systemctl restart anetbbs-web
Web interface loads but telnet/SSH won't connect
Check the firewall and that the terminal service is running:
sudo ufw status
sudo systemctl status anetbbs
sudo journalctl -u anetbbs -n 50
Out of disk space on SD card
Move data/ to the USB SSD (see above). Check usage:
df -h
du -sh ~/anetbbs/data/*/
Pi runs hot / throttles
Add a heatsink and fan. Check throttling:
vcgencmd get_throttled
# 0x0 = no throttling, anything else = problem
Low memory warnings
Add swap (Pi 5 shouldn't need this with 8 GB, but Pi 4 4 GB might):
sudo dphys-swapfile swapoff
sudo sed -i 's/CONF_SWAPSIZE=100/CONF_SWAPSIZE=1024/' /etc/dphys-swapfile
sudo dphys-swapfile setup
sudo dphys-swapfile swapon
Door Games on Pi
- In-browser DOS games (DOOM, Duke3D via EmulatorJS): run in the user's browser —
no server-side binary needed. On Pi 3, these may not run well in a browser on the Pi
itself; users connecting from a PC browser will have no issues. - LORD (Synchronet JS via Node.js): works on Pi 3+ ARM.
- DOSBox doors: DOSBox-X has ARM builds — install from the DOSBox-X releases
page and setDOSBOX_PATHin.env. - Wine + door32 .exe doors: possible with box86/box64, but a project for
advanced sysops.
Confirmed Working Configurations
| Hardware | OS | Python | Status |
|---|---|---|---|
| Pi 5 8 GB | Ubuntu 24.04 Server 64-bit | 3.12 | ✅ Fully confirmed |
| Pi 5 8 GB | Raspberry Pi OS Lite 64-bit (Bookworm) | 3.11 | ✅ Confirmed |
| Pi 4 8 GB | Ubuntu 22.04 Server 64-bit | 3.10 | ✅ Confirmed |
| Pi 4 4 GB | Raspberry Pi OS Lite 64-bit (Bookworm) | 3.11 | ✅ Works, tight on RAM |
| Pi 4 4 GB | Ubuntu 24.04 Server 64-bit | 3.12 | ✅ Confirmed |
| Pi 3B / 3B+ | Raspberry Pi OS Lite 32/64-bit | 3.11 | ✅ Works great — BBS runs fast; in-browser DOS games not confirmed |
| Pi 2 / earlier | any | — | ⚠ Not tested |