Installer for running the NetBird agent as a Docker container on WAGO PLCs/Panels running Linux/PTXdist.
This project is designed for WAGO devices where:
/homeRepository:
https://github.com/thomassandberg/netbird-docker-installer-wago
Installer URL:
https://raw.githubusercontent.com/thomassandberg/netbird-docker-installer-wago/refs/heads/main/netbird/install
The scripts and WAGOupload files in this repository are provided as examples and must be reviewed, tested and adapted to your own WAGO device, firmware version, network setup and operational requirements.
The files in the wagoupload/ directory are example files only. They may need changes before they are used in a real firmware update workflow.
Running the installer, repair scripts, update scripts or WAGOupload scripts is done entirely at your own risk.
Test everything on a non-critical device before using it in production.
/home/netbird for persistent NetBird data/home/netbird/stateupdate-netbird <version>netbird statusrepair mode after WAGO firmware updateswagoupload/ directorynetbird-docker-installer-wago/
netbird/
install
wagoupload/
run-netbird-restore.sh
wupload.cfg
README.md
netbird/installMain installer script.
It supports:
install <NETBIRD_TAG> <SETUP_KEY> [MANAGEMENT_URL] [HEALTHCHECK_IP]
repair
wagoupload/run-netbird-restore.shExample script intended for WAGOupload/.appload.
It runs the local NetBird restore script on the controller:
/bin/sh /home/netbird/repair/restore-host-hooks.sh
wagoupload/wupload.cfgExample WAGOupload configuration snippet showing how to run run-netbird-restore.sh at the end of a WAGOupload workflow.
The installer creates the following structure:
/home/netbird/
state/
config/
netbird.env
netbird.conf
scripts/
run-netbird.sh
healthcheck-netbird.sh
watchdog-netbird.sh
upgrade-netbird.sh
boot-netbird.sh
versions/
current-version
previous-version
pending-upgrade
logs/
upgrade.log
watchdog.log
restore-host-hooks.log
wagoupload-restore.log
lock/
tmp/
repair/
restore-host-hooks.sh
The most important directory is:
/home/netbird/state
This directory is mounted into the NetBird container as:
/var/lib/netbird
Do not delete /home/netbird/state unless you intentionally want to remove the NetBird peer identity from the device.
Run the installer as root.
The WAGO device must have:
/dev/net/tuncrontabCheck Docker:
docker --version
docker ps
Check TUN:
ls -l /dev/net/tun
The installer supports two modes:
install <NETBIRD_TAG> <SETUP_KEY> [MANAGEMENT_URL] [HEALTHCHECK_IP]
repair
Use install for first-time installation.
Use repair after firmware updates or if cronjobs, wrappers, symlinks or init scripts have disappeared while /home/netbird is still intact.
curl -fsSLk https://raw.githubusercontent.com/thomassandberg/netbird-docker-installer-wago/refs/heads/main/netbird/install \
| sh -s -- install <NETBIRD_TAG> <SETUP_KEY> [MANAGEMENT_URL] [HEALTHCHECK_IP]
Arguments:
| Argument | Required | Description |
|---|---|---|
NETBIRD_TAG |
yes | NetBird Docker image tag, for example 0.72.2 |
SETUP_KEY |
yes | NetBird setup key |
MANAGEMENT_URL |
no | Self-hosted NetBird management URL |
HEALTHCHECK_IP |
no | Optional IP to ping through NetBird during healthcheck |
If using NetBird Cloud and you also want to provide a healthcheck IP, pass an empty string for MANAGEMENT_URL:
""
Use this when using NetBird Cloud and no healthcheck IP:
curl -fsSLk https://raw.githubusercontent.com/thomassandberg/netbird-docker-installer-wago/refs/heads/main/netbird/install \
| sh -s -- install 0.72.2 SETUP_KEY
Example:
curl -fsSLk https://raw.githubusercontent.com/thomassandberg/netbird-docker-installer-wago/refs/heads/main/netbird/install \
| sh -s -- install 0.72.2 AAAAAA-BBBBBB-CCCCCC
Use this when using NetBird Cloud and you want the watchdog to verify connectivity to a known NetBird IP:
curl -fsSLk https://raw.githubusercontent.com/thomassandberg/netbird-docker-installer-wago/refs/heads/main/netbird/install \
| sh -s -- install 0.72.2 SETUP_KEY "" 100.80.10.1
Example:
curl -fsSLk https://raw.githubusercontent.com/thomassandberg/netbird-docker-installer-wago/refs/heads/main/netbird/install \
| sh -s -- install 0.72.2 AAAAAA-BBBBBB-CCCCCC "" 100.80.10.1
Use this when running your own NetBird management server:
curl -fsSLk https://raw.githubusercontent.com/thomassandberg/netbird-docker-installer-wago/refs/heads/main/netbird/install \
| sh -s -- install 0.72.2 SETUP_KEY https://netbird.example.com
Example:
curl -fsSLk https://raw.githubusercontent.com/thomassandberg/netbird-docker-installer-wago/refs/heads/main/netbird/install \
| sh -s -- install 0.72.2 AAAAAA-BBBBBB-CCCCCC https://netbird.example.com
Use this when running your own NetBird management server and you want the watchdog to verify connectivity to a known NetBird IP:
curl -fsSLk https://raw.githubusercontent.com/thomassandberg/netbird-docker-installer-wago/refs/heads/main/netbird/install \
| sh -s -- install 0.72.2 SETUP_KEY https://netbird.example.com 100.80.10.1
Example:
curl -fsSLk https://raw.githubusercontent.com/thomassandberg/netbird-docker-installer-wago/refs/heads/main/netbird/install \
| sh -s -- install 0.72.2 AAAAAA-BBBBBB-CCCCCC https://netbird.example.com 100.80.10.1
NetBird is started as a Docker container named:
netbird
The container is started with:
--network host
--restart unless-stopped
--cap-add NET_ADMIN
--cap-add SYS_ADMIN
--cap-add SYS_RESOURCE
--device /dev/net/tun:/dev/net/tun
--env-file /home/netbird/config/netbird.env
-v /home/netbird/state:/var/lib/netbird
The container image is:
netbirdio/netbird:<NETBIRD_TAG>
WAGO firmware updates may remove files outside /home, for example:
/usr/local/bin/update-netbird
/usr/bin/update-netbird
/usr/local/bin/netbird
/usr/bin/netbird
/etc/init.d/netbird
root crontab entry
The NetBird state and configuration should still exist under:
/home/netbird
If /home/netbird is still intact, run repair mode:
curl -fsSLk https://raw.githubusercontent.com/thomassandberg/netbird-docker-installer-wago/refs/heads/main/netbird/install \
| sh -s -- repair
Repair mode does not require:
Repair mode preserves:
/home/netbird/state
/home/netbird/config/netbird.env
/home/netbird/config/netbird.conf
/home/netbird/versions/current-version
Repair mode restores:
/home/netbird/scripts/*
/home/netbird/repair/restore-host-hooks.sh
/usr/local/bin/update-netbird
/usr/bin/update-netbird
/usr/local/bin/netbird
/usr/bin/netbird
/etc/init.d/netbird
root crontab entry
Repair mode also checks whether NetBird is healthy. If not, it tries to recreate the container using the version stored in:
/home/netbird/versions/current-version
The installer creates a local repair script:
/home/netbird/repair/restore-host-hooks.sh
This script is intended for WAGOupload/.appload after firmware updates.
It does not require:
It restores local host hooks from files already stored under /home/netbird.
The command to run is:
/bin/sh /home/netbird/repair/restore-host-hooks.sh
You can also run it manually:
/home/netbird/repair/restore-host-hooks.sh
It restores:
/usr/local/bin/update-netbird
/usr/bin/update-netbird
/usr/local/bin/netbird
/usr/bin/netbird
/etc/init.d/netbird
root crontab entry
It also tries to start or repair the NetBird container using the version stored in:
/home/netbird/versions/current-version
Check the local repair log:
tail -100 /home/netbird/logs/restore-host-hooks.log
This repository includes example WAGOupload files in:
wagoupload/
Files:
wagoupload/run-netbird-restore.sh
wagoupload/wupload.cfg
These files are examples that can be used when creating a WAGOupload .appload package for firmware upgrades.
The goal is:
Firmware update
→ /home is preserved
→ WAGOupload runs run-netbird-restore.sh
→ run-netbird-restore.sh runs /home/netbird/repair/restore-host-hooks.sh
→ NetBird host hooks are restored
→ NetBird container is started or repaired
Inside the .appload package:
wupload.cfg
run-netbird-restore.sh
or, if you prefer a subdirectory:
wupload.cfg
scripts/
run-netbird-restore.sh
Make sure the path in wupload.cfg matches the actual location of the script inside the .appload package.
wagoupload/run-netbird-restore.shThe repository includes this example file:
wagoupload/run-netbird-restore.sh
Recommended content:
#!/bin/sh
LOG="/home/netbird/logs/wagoupload-restore.log"
mkdir -p /home/netbird/logs
echo "$(date -Iseconds) WAGOupload NetBird restore started" >> "$LOG"
if [ ! -f /home/netbird/repair/restore-host-hooks.sh ]; then
echo "$(date -Iseconds) ERROR: /home/netbird/repair/restore-host-hooks.sh not found" >> "$LOG"
exit 1
fi
/bin/sh /home/netbird/repair/restore-host-hooks.sh >> "$LOG" 2>&1
RC="$?"
echo "$(date -Iseconds) WAGOupload NetBird restore finished with exit code $RC" >> "$LOG"
exit "$RC"
Important:
#!/bin/sh#!/bin/bashLFCRLFOn Linux, WSL or macOS, fix line endings and permissions before packaging:
sed -i 's/\r$//' wagoupload/run-netbird-restore.sh
chmod +x wagoupload/run-netbird-restore.sh
Verify that the file does not contain CRLF:
file wagoupload/run-netbird-restore.sh
Good:
POSIX shell script, ASCII text executable
Bad:
POSIX shell script, ASCII text executable, with CRLF line terminators
You can also inspect the first line:
head -1 wagoupload/run-netbird-restore.sh | od -An -t x1
Good:
23 21 2f 62 69 6e 2f 73 68 0a
Bad:
23 21 2f 62 69 6e 2f 73 68 0d 0a
If WAGOupload logs an error like this:
sudo: unable to execute /tmp/run-netbird-restore.sh: No such file or directory
even though the file was uploaded successfully, the most likely cause is CRLF line endings or an invalid shebang.
wagoupload/wupload.cfgThe repository includes this example file:
wagoupload/wupload.cfg
If run-netbird-restore.sh is placed in the root of the .appload package, use:
[ExecuteScriptAtEnd]
File1=run-netbird-restore.sh
Timeout1=600
If run-netbird-restore.sh is placed in a scripts directory inside the .appload package, use:
[ExecuteScriptAtEnd]
File1=scripts\run-netbird-restore.sh
Timeout1=600
Use the variant that matches your .appload layout.
The timeout is set to 600 seconds because the repair script may need to wait for Docker, restore cron/wrappers, recreate the NetBird container and run healthchecks.
If [ExecuteScriptAtEnd] already exists, add it as the next file number:
[ExecuteScriptAtEnd]
File1=scripts\existing-script.sh
Timeout1=300
File2=run-netbird-restore.sh
Timeout2=600
.appload packageA practical workflow is:
1. Create or open the WAGOupload application package.
2. Add run-netbird-restore.sh to the package.
3. Add or merge the [ExecuteScriptAtEnd] section in wupload.cfg.
4. Make sure the File1 path in wupload.cfg matches the script location.
5. Make sure run-netbird-restore.sh uses LF line endings.
6. Run the firmware update with this .appload package.
7. WAGOupload uploads and executes run-netbird-restore.sh at the end.
8. NetBird repair is triggered automatically.
Recommended command executed indirectly by WAGOupload:
/bin/sh /home/netbird/repair/restore-host-hooks.sh
WAGOupload should not need the NetBird setup key.
WAGOupload should not need access to GitHub.
WAGOupload should not restore /var/lib/docker.
Recommended firmware update workflow:
1. NetBird is installed and working.
2. /home/netbird/repair/restore-host-hooks.sh exists and has been tested.
3. WAGOupload performs firmware update.
4. /home is preserved.
5. WAGOupload runs run-netbird-restore.sh at the end.
6. run-netbird-restore.sh runs:
/bin/sh /home/netbird/repair/restore-host-hooks.sh
7. restore-host-hooks.sh restores:
- update-netbird
- netbird CLI wrapper
- /etc/init.d/netbird
- watchdog cron entry
8. restore-host-hooks.sh starts or repairs the NetBird container.
9. Remote access through NetBird should come back.
Before relying on WAGOupload/.appload in production, test the local repair script manually on the controller:
/home/netbird/repair/restore-host-hooks.sh
Verify:
which update-netbird
which netbird
crontab -l
docker ps | grep netbird
netbird status
ip link show wt0
tail -100 /home/netbird/logs/restore-host-hooks.log
You can simulate a firmware update removing host-side files:
rm -f /usr/bin/update-netbird
rm -f /usr/bin/netbird
rm -f /usr/local/bin/update-netbird
rm -f /usr/local/bin/netbird
rm -f /etc/init.d/netbird
crontab -l 2>/dev/null | grep -v "/home/netbird/scripts/watchdog-netbird.sh" | crontab -
Then run local repair:
/home/netbird/repair/restore-host-hooks.sh
Verify:
which update-netbird
which netbird
ls -l /etc/init.d/netbird
crontab -l
netbird status
Copy the WAGOupload script to the controller:
scp wagoupload/run-netbird-restore.sh root@<wago-ip>:/tmp/run-netbird-restore.sh
SSH to the controller:
ssh root@<wago-ip>
Run it explicitly through /bin/sh:
/bin/sh /tmp/run-netbird-restore.sh
If this works, but executing the file directly fails:
/tmp/run-netbird-restore.sh
then the problem is likely one of:
Fix locally and rebuild the .appload package:
sed -i 's/\r$//' wagoupload/run-netbird-restore.sh
chmod +x wagoupload/run-netbird-restore.sh
If you want to inspect the installer before running it:
curl -fsSLk https://raw.githubusercontent.com/thomassandberg/netbird-docker-installer-wago/refs/heads/main/netbird/install \
-o /home/install-netbird-master.sh
Make it executable:
chmod 700 /home/install-netbird-master.sh
Run first-time installation with NetBird Cloud:
/home/install-netbird-master.sh install 0.72.2 SETUP_KEY
Run first-time installation with NetBird Cloud and healthcheck IP:
/home/install-netbird-master.sh install 0.72.2 SETUP_KEY "" 100.80.10.1
Run first-time installation with self-hosted NetBird:
/home/install-netbird-master.sh install 0.72.2 SETUP_KEY https://netbird.example.com
Run first-time installation with self-hosted NetBird and healthcheck IP:
/home/install-netbird-master.sh install 0.72.2 SETUP_KEY https://netbird.example.com 100.80.10.1
Run repair mode:
/home/install-netbird-master.sh repair
Check container:
docker ps | grep netbird
Check NetBird status using the host wrapper:
netbird status
Equivalent command without wrapper:
docker exec netbird netbird status
Check NetBird interface:
ip link show wt0
Check watchdog cron entry:
crontab -l
Expected cron entry:
* * * * * /home/netbird/scripts/watchdog-netbird.sh >/dev/null 2>&1
Check update command:
which update-netbird
ls -l /usr/bin/update-netbird
ls -l /usr/local/bin/update-netbird
Check NetBird CLI wrapper:
which netbird
ls -l /usr/bin/netbird
ls -l /usr/local/bin/netbird
Check configured healthcheck IP:
grep HEALTHCHECK_IP /home/netbird/config/netbird.conf
Check current installed NetBird version:
cat /home/netbird/versions/current-version
Check logs:
tail -100 /home/netbird/logs/watchdog.log
tail -100 /home/netbird/logs/upgrade.log
tail -100 /home/netbird/logs/restore-host-hooks.log
tail -100 /home/netbird/logs/wagoupload-restore.log
After installation, the installer provides this command:
update-netbird
Usage:
update-netbird <NEW_NETBIRD_TAG>
Example:
update-netbird 0.72.3
The update runs in the background using the local upgrade script:
/home/netbird/scripts/upgrade-netbird.sh
The update process:
Follow update progress:
tail -f /home/netbird/logs/upgrade.log
Check current version after update:
cat /home/netbird/versions/current-version
Check NetBird status:
netbird status
The installer creates a host-side netbird wrapper.
This means you can run:
netbird status
instead of:
docker exec netbird netbird status
Other examples:
netbird version
netbird debug
The wrapper is installed here:
/usr/local/bin/netbird
And symlinked here:
/usr/bin/netbird
The wrapper executes the NetBird CLI inside the running container:
docker exec netbird netbird "$@"
If the wrapper is missing after a firmware update, restore it with:
/home/netbird/repair/restore-host-hooks.sh
If needed, manually roll back to the previous version:
/home/netbird/scripts/run-netbird.sh "$(cat /home/netbird/versions/previous-version)"
Verify:
netbird status
ip link show wt0
The installer can be run again.
For a normal first-time installation or full reconfiguration, use:
curl -fsSLk https://raw.githubusercontent.com/thomassandberg/netbird-docker-installer-wago/refs/heads/main/netbird/install \
| sh -s -- install 0.72.2 SETUP_KEY https://netbird.example.com 100.80.10.1
This will overwrite:
/home/netbird/config/netbird.env
/home/netbird/config/netbird.conf
/home/netbird/scripts/*
/home/netbird/repair/restore-host-hooks.sh
/usr/local/bin/update-netbird
/usr/bin/update-netbird
/usr/local/bin/netbird
/usr/bin/netbird
It will not delete:
/home/netbird/state
Because /home/netbird/state is preserved, the NetBird peer identity should remain intact.
For firmware repair only, use:
curl -fsSLk https://raw.githubusercontent.com/thomassandberg/netbird-docker-installer-wago/refs/heads/main/netbird/install \
| sh -s -- repair
Repair mode does not overwrite:
/home/netbird/config/netbird.env
/home/netbird/config/netbird.conf
/home/netbird/state
For fully local repair without GitHub access, use:
/home/netbird/repair/restore-host-hooks.sh
After the device has been registered successfully, you may remove the setup key from:
/home/netbird/config/netbird.env
For NetBird Cloud, the file can be empty or removed if the existing state is valid.
For self-hosted NetBird, keep:
NB_MANAGEMENT_URL=https://netbird.example.com
Restart NetBird:
docker restart netbird
Verify:
netbird status
If you later run install mode again, the setup key will be written back to /home/netbird/config/netbird.env.
If you only need to restore cron, wrapper, symlink or init script after firmware updates, use repair mode or local restore mode instead.
curl -kMost examples use:
curl -fsSLk
The -k option disables TLS certificate verification.
This can be useful on embedded devices with missing or outdated CA certificates, but it is less secure.
If the device has working CA certificates, prefer:
curl -fsSL https://raw.githubusercontent.com/thomassandberg/netbird-docker-installer-wago/refs/heads/main/netbird/install \
| sh -s -- install 0.72.2 SETUP_KEY
docker ps
If this fails, Docker is not available or not running.
ls -l /dev/net/tun
NetBird requires /dev/net/tun.
docker logs --tail=200 netbird
netbird status
Equivalent command:
docker exec netbird netbird status
tail -100 /home/netbird/logs/watchdog.log
tail -100 /home/netbird/logs/upgrade.log
tail -100 /home/netbird/logs/restore-host-hooks.log
tail -100 /home/netbird/logs/wagoupload-restore.log
cat /home/netbird/config/netbird.conf
cat /home/netbird/config/netbird.env
Check where it is installed:
ls -l /usr/local/bin/update-netbird
ls -l /usr/bin/update-netbird
echo $PATH
On WAGO/PTXdist, /usr/local/bin may not be in PATH.
The installer creates this symlink:
/usr/bin/update-netbird -> /usr/local/bin/update-netbird
If it is missing, run repair mode:
curl -fsSLk https://raw.githubusercontent.com/thomassandberg/netbird-docker-installer-wago/refs/heads/main/netbird/install \
| sh -s -- repair
Or run fully local repair:
/home/netbird/repair/restore-host-hooks.sh
Check where it is installed:
ls -l /usr/local/bin/netbird
ls -l /usr/bin/netbird
echo $PATH
The installer creates this symlink:
/usr/bin/netbird -> /usr/local/bin/netbird
If it is missing, run:
/home/netbird/repair/restore-host-hooks.sh
Example error:
sudo: unable to execute /tmp/run-netbird-restore.sh: No such file or directory
If WAGOupload says the file was uploaded successfully but execution fails, the most likely causes are:
run-netbird-restore.sh has Windows CRLF line endings#!/bin/sh/bin/bash, which may not existFix locally:
sed -i 's/\r$//' wagoupload/run-netbird-restore.sh
chmod +x wagoupload/run-netbird-restore.sh
Verify:
file wagoupload/run-netbird-restore.sh
Then rebuild the .appload package and try again.
For NetBird Cloud with healthcheck IP:
curl -fsSLk https://raw.githubusercontent.com/thomassandberg/netbird-docker-installer-wago/refs/heads/main/netbird/install \
| sh -s -- install 0.72.2 SETUP_KEY "" 100.80.10.1
For self-hosted NetBird with healthcheck IP:
curl -fsSLk https://raw.githubusercontent.com/thomassandberg/netbird-docker-installer-wago/refs/heads/main/netbird/install \
| sh -s -- install 0.72.2 SETUP_KEY https://netbird.example.com 100.80.10.1
After WAGO firmware update, preferably through WAGOupload/.appload:
/bin/sh /home/netbird/repair/restore-host-hooks.sh
Or by using the included WAGOupload example files:
wagoupload/run-netbird-restore.sh
wagoupload/wupload.cfg
After WAGO firmware update, manually through GitHub repair mode:
curl -fsSLk https://raw.githubusercontent.com/thomassandberg/netbird-docker-installer-wago/refs/heads/main/netbird/install \
| sh -s -- repair
For later NetBird updates:
update-netbird 0.72.3
For NetBird status:
netbird status
The setup key should be treated as a secret.
The installer stores the setup key in:
/home/netbird/config/netbird.env
After successful registration, consider removing the setup key from that file.
Avoid using latest as the NetBird Docker tag. Always use a specific version, for example:
0.72.2
This makes rollback predictable.
Do not delete:
/home/netbird/state
unless you intentionally want the device to lose its NetBird identity.
Do not restore or overwrite:
/var/lib/docker
as part of WAGOupload/.appload unless you have a very specific reason and have tested it thoroughly.
WAGOupload/.appload should only run the local restore script:
/bin/sh /home/netbird/repair/restore-host-hooks.sh
MIT