Complete Guide: Wake-on-LAN, Jellyfin & WireGuard on Debian

Secure Remote Media Server Setup

Table of Contents


Introduction

In this guide, I will walk you through setting up a secure remote media server using:

  • Wake-on-LAN (WOL) – remotely power on your Debian machine
  • Jellyfin – free, open-source media streaming
  • WireGuard VPN – secure encrypted access to your server

This setup allows you to:

  • Wake up your server from anywhere
  • Access Jellyfin securely from your phone
  • Stream content exactly as if you were on your home network
  • Avoid exposing sensitive services directly to the internet

Requirements

Before you begin, make sure you have:

  • A Debian-based server
  • A home router with NAT/PAT support
  • An Android smartphone
  • Basic terminal knowledge

Wake-on-LAN (WOL)

Step 1 — Enable WOL in the BIOS

Look for options such as:

  • Wake on LAN
  • Power on by PCI-E
  • Resume by LAN

Enable the appropriate setting.


Step 2 — Enable WOL on Debian

Install ethtool:

sudo apt install ethtool

Enable WOL on your network interface:

sudo ethtool -s eth0 wol g

(Replace eth0 with your actual interface using ip link.)


Step 3 — Make WOL Persistent

Create /etc/systemd/system/wol.service:

[Unit]
Description=Wake-on-LAN configuration
After=network.target

[Service]
Type=oneshot
ExecStart=/sbin/ethtool -s eth0 wol g

[Install]
WantedBy=multi-user.target

Enable the service:

sudo systemctl enable wol

Step 4 — Configure Router & Mobile App

On your router:

  • Assign a static DHCP lease
  • Forward UDP 9 → port 9 of the server

On Android (app: Wake On LAN – by MR-Webb):

  • Name: any label
  • Group: Bookmarked
  • MAC address: server NIC MAC
  • Broadcast/Host: server LAN IP
  • Public IP: router’s WAN IP
  • Port: 9

Installing and Configuring Jellyfin

Step 1 — Install Jellyfin

curl https://repo.jellyfin.org/install-debuntu.sh | sudo bash
sudo systemctl enable jellyfin
sudo systemctl start jellyfin

Step 2 — Access the Web Interface

Navigate to:

http://YOUR_SERVER_IP:8096

Follow the setup steps:

  • Create your account
  • Add media libraries (Movies, Series, Music, etc.)

!!! Warning: Do NOT expose Jellyfin directly to the internet !!!

Always place Jellyfin behind a VPN such as WireGuard.


WireGuard VPN Setup

Step 1 — Install WireGuard

sudo apt install wireguard

Install the official Android app:

WireGuard – WireGuard Development Team


Step 2 — Generate Keys

Server keys:

wg genkey | tee server_private.key | wg pubkey > server_public.key

Phone keys:

wg genkey | tee phone_private.key | wg pubkey > phone_public.key

Display keys:

cat server_private.key
cat server_public.key
cat phone_private.key
cat phone_public.key

Never share private keys.


Step 3 — Configure /etc/wireguard/wg0.conf

Example VPN subnet:

  • VPN network: 10.10.0.0/24
  • Server: 10.10.0.1
  • Phone: 10.10.0.2
[Interface]
Address = 10.10.0.1/24
ListenPort = 51820
PrivateKey = <SERVER_PRIVATE_KEY>

PostUp   = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o enp0s31f6 -j MASQUERADE; ip6tables -A INPUT -p udp --dport 51820 -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o enp0s31f6 -j MASQUERADE; ip6tables -D INPUT -p udp --dport 51820 -j ACCEPT

SaveConfig = true

[Peer]
PublicKey = <PHONE_PUBLIC_KEY>
AllowedIPs = 10.10.0.2/32

Replace enp0s31f6 with your interface name.


Step 4 — Enable IP Forwarding

echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system

Step 5 — Configure Router Port Forwarding

Forward:

  • UDP 51820 → server LAN IP

Step 6 — Start WireGuard

sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
sudo wg

Step 7 — Configure WireGuard on Android

[Interface]
PrivateKey = <PHONE_PRIVATE_KEY>
Address = 10.10.0.2/32
DNS = 1.1.1.1

[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = YOUR_PUBLIC_IP:51820
AllowedIPs = 192.168.1.0/24, 10.10.0.0/24
PersistentKeepalive = 25

Step 8 — Generate QR Code (Optional)

sudo apt install qrencode
qrencode -t ansiutf8 < phone.conf

Scan it directly in the WireGuard app.


Testing the Setup

  1. Send WOL packet from your phone
  2. Wait for the Debian server to boot
  3. Activate WireGuard tunnel on your phone
  4. Open Jellyfin via LAN IP:
http://192.168.1.50:8096

(Replace with your own server IP)


Troubleshooting

Common issues:

  • Incorrect router port forwarding
  • Wrong key pasted in configs
  • Firewall blocking VPN traffic

UFW Firewall Configuration (if applicable)

Allow WireGuard:

sudo ufw allow 51820/udp

Allow VPN subnet:

sudo ufw allow from 10.10.0.0/24

Add NAT rules in /etc/ufw/before.rules before *filter:

# WireGuard NAT masquerading
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 10.10.0.0/24 -o enp0s31f6 -j MASQUERADE
COMMIT

Ensure forwarding rules are present:

-A ufw-before-forward -i wg0 -j ACCEPT
-A ufw-before-forward -o wg0 -j ACCEPT

Restart UFW:

sudo ufw disable
sudo ufw enable

#jellyfin #wireguard #debian #wol #media #setup

Trick grep / egrep to always display filename

If you use grep / egrep on a single file, it will display the matched patterns, but not the filename nor the line number.

This can be boring in case you encapsulate a grep / egrep call inside i.e a find:

find ./mydir -type f -name '*.msg' -exec grep 'mysearch' {} \;

In that case it will only print the match and you will not know in which file.

The trick is to add a dummy filename as a second file to search. Doing so will trick grep / egrep into thinking you are searching in multiples files, thus displaying the filename on each match.
/dev/null is the perfect target for the second dummy file as it will never match anything.

Example:

find ./mydir -type f -name ‘*.msg’ -exec grep ‘mysearch’ {} /dev/null \;

Bonus / reminder:

In grep / egrep use the -n option to also display the match line number

Example:

find ./mydir -type f -name ‘*.msg’ -exec grep -n ‘mysearch’ {} /dev/null \;

#sysadmin #tips #tricks #grep #egrep

KAFKA

KAFKA is in all mouths these days. So I share here, also as a reminder for me, a couple of #KAFKA ressources that looked helpful to me.

TLDR:

Do not use KAFKA for:

-“Little” Data flows (overkill)

-Streaming ETL (handling transformation is a hassle)

-Store and process large files (images, videos, proprietary files, etc.)

https://www.kai-waehner.de/blog/2020/08/07/apache-kafka-handling-large-messages-and-files-for-image-video-audio-processing/

https://www.upsolver.com/blog/apache-kafka-use-cases-when-to-use-not

https://kafka.apache.org/intro

https://docs.confluent.io/kafka-clients/librdkafka/current/overview.html

https://docs.confluent.io/platform/current/clients/librdkafka/html/md_INTRODUCTION.html

Cargo Cult Sytem Administrator

And some other types too.

Fun article to read here: https://blog.lastinfirstout.net/2009/11/cargo-cult-system-administration.html

One particularly right: “Asserting that [Technology O] or [Platform L] or [Methodology A] is inherently superior to all others and blindly applying it to all problems. When you make such claims, are you applying science or religion?”

“It’s easy to fall into cargo cult mode.
Just re-boot it, it’ll be fine.” – Michael Janke

Clean cache to reclaim memory without a reboot

Tip submitted by my friend J.K when we were in need of a way to recover reserved cache space without booting.


In root:

PageCache only:

sync ; echo 1 > /proc/sys/vm/drop_caches

Dentries and inodes:

sync ; echo 2 > /proc/sys/vm/drop_caches

PageCache, Dentries and inodes:
(not recommanded in production as it forces a full cache rebuilt. you may still need it):

sync ; echo 3 > /proc/sys/vm/drop_caches

In case you need to do it in a sudo:

echo 3 | sudo tee /proc/sys/vm/drop_caches

Syslog, rsyslogd: change output format to display log level and facilities

If ever you need to sort log messages by their log level yo may need to change the default output format of the syslog.
For rsyslog it’s located in /etc/rsyslogd.conf. Add these lines after the line “$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat” :

$template precise,”%timegenerated% %HOSTNAME% {%syslogpriority%,%syslogfacility%} %syslogtag% %msg%\n”
$ActionFileDefaultTemplate precise

Then restart rsyslogd:
sudo service rsyslog restart

#rsyslogd #syslog #log #loglevel #facilities