What We’re Actually Building Here
PPPoE server work sits in a layer most sysadmins never go near: session negotiation, IP pool management, and per-user authentication at the protocol layer. It’s the operational core of real ISP infrastructure. If you’re standing up a small ISP, a campus network, or a lab that mirrors carrier-grade setup — this is the work you’re signing up for.
I deployed this in production: accel-ppp with local auth on a managed network running 150 concurrent PPPoE sessions. It ran without major intervention for over a year. When I later added RADIUS for centralized user management, the cutover took less than an afternoon and didn’t disrupt existing sessions.
Approach Comparison
Three tools dominate the PPPoE-on-Linux space. Which one fits depends on your session count and how much operational complexity you want to carry.
Option 1: rp-pppoe (Classic pppoe-server)
rp-pppoe is the original userspace PPPoE server, wrapping Linux’s pppd daemon. Each session spawns a dedicated pppd process — fine for a handful of users, but that model collapses fast under load. Config is scattered across /etc/ppp/options, chap-secrets, and the pppoe-server startup flags. Authentication goes through PAP/CHAP locally or via RADIUS. Debugging means chasing log lines from multiple processes at the same time.
Option 2: accel-ppp
accel-ppp is a kernel-accelerated multi-protocol access server. A single daemon handles PPPoE, L2TP, and PPTP through a unified config file. Sessions run in kernel space, so throughput scales well beyond what rp-pppoe’s per-process model can reach. Built-in RADIUS support, a TCP management CLI, IP pool configuration, and per-session rate limiting all come standard. For anything beyond a test lab, this is the right tool.
Option 3: RouterOS VM
Some teams run Mikrotik RouterOS as a VM purely as the PPPoE concentrator. If your team already knows RouterOS, the learning curve is shorter. But you’re now maintaining a full separate OS just to handle PPPoE — with licensing cost on top. On a Linux-native stack, accel-ppp does the same job natively with far less overhead.
Pros and Cons
rp-pppoe
- Pros: Simple to set up, widely documented, works with stock
pppd, good for learning the protocol - Cons: One OS process per session (heavy CPU at scale), config fragmented across multiple files, hard to manage live sessions, hits a wall around 100 concurrent users
accel-ppp
- Pros: Kernel-accelerated, single daemon, built-in RADIUS client, scales to thousands of sessions, live session management via CLI, rate limiting (shaper) built in
- Cons: Requires compiling from source on most distros, kernel module dependency, documentation is thinner than rp-pppoe
RouterOS VM
- Pros: GUI management, familiar to engineers with a Mikrotik background
- Cons: Licensing cost, resource overhead for a full VM, not native to Linux tooling
Recommended Setup
Use accel-ppp for anything beyond a small test deployment. Kernel-level session handling alone makes it worth the slightly more involved build. Start with local chap-secrets for auth, then move to FreeRADIUS when you need centralized user management or per-user plan enforcement.
The recommended stack:
- accel-ppp as the PPPoE concentrator
- Local auth (
/etc/ppp/chap-secrets) for dev and small deployments - FreeRADIUS for production with multiple users and plans
- iptables MASQUERADE for NAT to uplink
- systemd service for auto-start and restart on failure
Implementation Guide
Step 1: Build and Install accel-ppp
accel-ppp isn’t in most distribution repositories, so you’ll compile from source:
# Install build dependencies (Ubuntu/Debian)
sudo apt update && sudo apt install -y cmake libpcre3-dev libssl-dev \
libevent-dev libcurl4-openssl-dev git build-essential
git clone https://github.com/accel-ppp/accel-ppp.git
cd accel-ppp
mkdir build && cd build
cmake -DKDIR=/usr/src/linux-headers-$(uname -r) \
-DCMAKE_INSTALL_PREFIX=/usr \
-DRADIUS=TRUE \
..
make -j$(nproc)
sudo make install
Load the kernel module and persist it across reboots:
sudo modprobe pppoe
echo "pppoe" | sudo tee -a /etc/modules
Step 2: Configure accel-ppp
Everything lives in /etc/accel-ppp.conf. Here’s a working base config:
[core]
thread-count=4
log-error=/var/log/accel-ppp/core.log
[modules]
log_file
pppoe
auth_mschap_v2
auth_chap_md5
auth_pap
ippool
shaper
[log]
log-file=/var/log/accel-ppp/accel-ppp.log
log-emerg=/var/log/accel-ppp/emerg.log
copy=1
[pppoe]
interface=eth1 # Interface on the client-facing (access) side
ac-name=MyISP
service-name=internet
verbose=1
[dns]
dns1=8.8.8.8
dns2=1.1.1.1
[ip-pool]
gw-ip-address=10.10.0.1 # IP assigned to the server side of each session
10.10.1.0/24 # Client IP pool
10.10.2.0/24
[ppp]
mtu=1492
mru=1492
ccp=0
min-mtu=1280
unit-priv=1
[auth]
chap-secrets=/etc/ppp/chap-secrets
[cli]
verbose=1
mode=tcp
port=2000
timeout=0
sudo mkdir -p /var/log/accel-ppp
sudo chown nobody:nogroup /var/log/accel-ppp
Step 3: Add Users for Local Authentication
chap-secrets uses standard pppd format. The fourth column pins a specific IP to a user, or use * to assign from the pool:
# /etc/ppp/chap-secrets
# client server secret IP addresses
user1 * password123 *
user2 * securepass *
admin * adminpass 10.10.1.10 # Fixed IP
The file holds plaintext credentials — lock it down:
sudo chmod 600 /etc/ppp/chap-secrets
sudo chown root:root /etc/ppp/chap-secrets
Step 4: Enable IP Forwarding and NAT
PPPoE clients route all traffic through your server, so forwarding must be on. Replace eth0 with your uplink interface:
sudo sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
# NAT for internet access
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# Clamp MSS to avoid MTU issues with large transfers
sudo iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
-j TCPMSS --clamp-mss-to-pmtu
# Persist rules
sudo apt install iptables-persistent
sudo netfilter-persistent save
Step 5: Create a systemd Service and Start
cat <<EOF | sudo tee /etc/systemd/system/accel-ppp.service
[Unit]
Description=accel-ppp access server
After=network.target
[Service]
ExecStart=/usr/sbin/accel-pppd -c /etc/accel-ppp.conf
Restart=on-failure
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now accel-ppp
sudo systemctl status accel-ppp
Step 6: Test a Client Connection
From a Linux machine on the same Layer 2 segment as eth1:
sudo apt install pppoe
# Configure a PPPoE client connection
sudo pppd plugin rp-pppoe eth0 \
user "user1" \
password "password123" \
noauth defaultroute usepeerdns persist
# Verify the PPP interface is up
ip addr show ppp0
ip route show
Then switch back to the server and verify the session registered:
sudo accel-cmd show sessions
# Active session, assigned IP, username, bytes in/out
Moving to FreeRADIUS for Production
Local auth works fine for small setups. When user count grows, swap it for FreeRADIUS: install it, define your users, then update [modules] and add a [radius] block to accel-ppp.conf:
[modules]
log_file
pppoe
radius
ippool
shaper
[radius]
server=127.0.0.1,secret=your_radius_secret,auth-port=1812,acct-port=1813
dae-server=127.0.0.1:3799,secret=your_radius_secret
timeout=3
max-try=3
acct-timeout=120
verbose=1
Drop the local [auth] block entirely. FreeRADIUS handles all authentication from that point. For per-user bandwidth enforcement, push WISPr-Bandwidth-Max-Up and WISPr-Bandwidth-Max-Down as RADIUS attributes — the same mechanism commercial ISPs use to enforce subscriber speed tiers.
Managing Live Sessions
No restart needed for session management. accel-ppp ships with a TCP CLI that gives you live control over every active connection:
# Connect to the management interface
sudo accel-cmd
# Useful commands inside the CLI:
show sessions
show sessions username user1
show stat
terminate username user1
terminate ip 10.10.1.15
Things That Will Bite You
A few things from my own deployment worth knowing before you go live:
- MTU mismatch causing silent failures: PPPoE adds 8 bytes of overhead, dropping effective MTU from 1500 to 1492. Small packets work fine. Large file transfers stall or fail completely. The MSS clamp in Step 4 covers most cases — also set your client interface MTU to 1492 explicitly.
- Kernel module missing: If sessions won’t establish at all, run
lsmod | grep pppoe. Some kernels also needppp_genericandppp_asyncloaded manually. - Interface on the wrong segment: The
pppoe interface=setting must be on the same Layer 2 broadcast domain as your clients. Clients on VLANs? Create VLAN subinterfaces first (eth1.100, etc.) and point accel-ppp at those instead of the raw physical interface. - File descriptor limits at scale: Every session consumes file descriptors.
LimitNOFILE=65535in the systemd unit covers this automatically. Running the daemon manually? Setulimit -n 65535in the shell before you start it.

