This episode covers deploying PPTP servers automatically: shell scripts for one-shot setup, bulk user creation, an Ansible role for repeatable configuration, and the considerations of running PPTP in a Docker container along with its limitations.

Configuring one PPTP server manually is easy. Deploying it to fifty servers at once is another story. Episode 12 covers automating setup, creating users in bulk, and deploying configuration consistently.
Automation is the skill that makes your work faster every time it is repeated. Although we are talking about legacy technology here, the principles in this episode apply to any service.
The simplest shell script automates the steps we did manually in episode 3:
#!/usr/bin/env bash
set -euo pipefail
apt-get update
apt-get install -y pptpd ppp
sysctl -w net.ipv4.ip_forward=1
sed -i 's/#localip 192.168.0.1/localip 192.168.1.10/' /etc/pptpd.conf
sed -i 's/#remoteip 192.168.0.234-238/remoteip 192.168.1.100-110/' /etc/pptpd.conf
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
systemctl enable pptpd
systemctl restart pptpdThe script above uses set -euo pipefail so it stops on any error. Note that the sed commands modify the configuration files that already shipped with the package — a common approach, but make sure the packaged file format does not change between versions.
When dozens of new users arrive at once, do not edit chap-secrets one by one. Use a loop:
for user in ali budi citra dewi; do
echo "$user pptpd \"P@ssw0rd-$user\" *" >> /etc/ppp/chap-secrets
doneThe loop above adds four users with different passwords. For a real environment, do not store static passwords in a script — pull them from a secure source or generate them randomly.
Run the script only once, or add a check so existing users are not rewritten:
for user in ali budi citra dewi; do
if ! grep -q "^$user " /etc/ppp/chap-secrets; then
echo "$user pptpd \"P@ssw0rd-$user\" *" >> /etc/ppp/chap-secrets
fi
doneThe grep -q "^$user " /etc/ppp/chap-secrets pattern makes the script idempotent: re-running it will not create duplicates.
For large infrastructure, Ansible is a more structured choice than shell scripts. A PPTP role contains configuration file templates and installation tasks:
- name: Install paket PPTP
ansible.builtin.apt:
name: [pptpd, ppp]
state: present
- name: Tulis konfigurasi pptpd.conf
ansible.builtin.template:
src: pptpd.conf.j2
dest: /etc/pptpd.conf
notify: Restart pptpd
- name: Aktifkan IP forwarding
ansible.builtin.sysctl:
name: net.ipv4.ip_forward
value: "1"
sysctl_file: /etc/sysctl.d/99-pptp.confAnsible playbooks are idempotent and self-documenting, so a server's state can always be brought back to what is expected. This is far easier to maintain than a collection of scripts.
Running pptpd in a container is aesthetically appealing, but there are important technical caveats. GRE is an IP protocol (not TCP/UDP), so the container must use host networking — ordinary port publishing cannot forward GRE.
docker run -d --name pptp --network host \
--privileged \
-v /etc/pptpd.conf:/etc/pptpd.conf:ro \
-v /etc/ppp/chap-secrets:/etc/ppp/chap-secrets:ro \
ghcr.io/example/pptp-serverThe --network host option is required because GRE needs direct access to the host's IP stack, and --privileged is usually needed because the container must manage kernel modules and interfaces. Both loosen container isolation.
Combining PPTP with Docker adds complexity without adding security — quite the opposite. For legacy workloads, running pptpd directly on a VM or host is simpler and easier to troubleshoot.
Episode 12 equipped you with automation patterns: setup shell scripts, idempotent bulk user creation, Ansible roles, and the container discussion with all its limitations.
Key takeaways:
set -euo pipefail to run safely and repeatedly.--network host because GRE is not TCP/UDP.In the next episode, episode 13, we will discuss PPTP security analysis and vulnerabilities — the 2012 MS-CHAPv2 brute-force, RC4 weaknesses in MPPE, unencrypted GRE, risk assessment, and a list of relevant CVEs and advisories.