How to Set Up a PPPoE Server on Linux: ISP-Grade IP Allocation and User Authentication

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

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 need ppp_generic and ppp_async loaded 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=65535 in the systemd unit covers this automatically. Running the daemon manually? Set ulimit -n 65535 in the shell before you start it.
Share: