Skip to main content

Linux Digital Signage Guide

Linux powers a significant portion of professional digital signage deployments, offering unmatched flexibility, reliability, and cost-effectiveness. From embedded systems to enterprise data centers, Linux provides the foundation for scalable signage networks.

Why Linux for Digital Signage?

Advantages

AdvantageDescription
No License FeesFree OS eliminates per-device costs
Long-Term SupportUbuntu LTS: 10 years, Debian: 5 years
CustomizableTailor to exact signage requirements
SecurityStrong security model, rapid patch availability
ReliabilityProven 24/7/365 server-grade stability
Hardware FlexibilityRuns on x86, ARM, embedded, servers
AutomationExcellent scripting and remote management
Open Source EcosystemVast software and tool availability

Considerations

ChallengeMitigation
Technical ComplexityUse purpose-built signage distros or detailed guides
Driver SupportChoose hardware with good Linux support
Learning CurveStart with familiar distributions (Ubuntu)
Vendor SupportUse commercial Linux or enterprise subscriptions
Desktop EnvironmentOften not needed; increases complexity

Distribution Selection

┌─────────────────────────────────────────────────────────────────────────────┐
│ Linux Distributions for Signage │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Ubuntu LTS (Recommended for Most) │
│ ├── Versions: 22.04 LTS, 24.04 LTS │
│ ├── Support: 10 years with Ubuntu Pro │
│ ├── Best for: General signage, enterprise deployments │
│ └── Pros: Excellent docs, huge community, good hardware support │
│ │
│ Debian Stable │
│ ├── Versions: Debian 12 (Bookworm) │
│ ├── Support: ~5 years │
│ ├── Best for: Stability-focused deployments │
│ └── Pros: Ultra-stable, minimal bloat, long testing period │
│ │
│ Raspberry Pi OS (for Pi hardware) │
│ ├── Based on: Debian │
│ ├── Best for: Raspberry Pi signage players │
│ └── See: Raspberry Pi Signage Guide │
│ │
│ Fedora IoT / RHEL Edge │
│ ├── Support: 10+ years (RHEL) │
│ ├── Best for: Enterprise with Red Hat standardization │
│ └── Pros: Immutable OS option, strong enterprise support │
│ │
│ Alpine Linux │
│ ├── Size: ~130MB │
│ ├── Best for: Containers, embedded, minimal footprint │
│ └── Pros: Security-focused, tiny size, fast boot │
│ │
│ Yocto/Buildroot (Custom Embedded) │
│ ├── Best for: OEMs, high-volume, specialized hardware │
│ └── Pros: Fully custom, minimal attack surface │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

Distribution Comparison

FeatureUbuntu LTSDebianAlpineFedora IoT
Ease of Setup★★★★★★★★★★★★★★★★
Stability★★★★★★★★★★★★★★★★★
Hardware Support★★★★★★★★★★★★★★★★
Documentation★★★★★★★★★★★★★★★★
Size~2GB~1GB~130MB~800MB
Boot Time~15-25s~15-20s~5-10s~10-15s
Package Freshness★★★★★★★★★★★★★★★★★

Installation & Base Setup

Ubuntu Server Installation (Headless)

# Download Ubuntu Server LTS
# Use Rufus/Etcher to create bootable USB

# During installation:
# - Select "Ubuntu Server (minimized)" for smallest footprint
# - Enable OpenSSH server
# - Skip snaps installation

# Post-installation setup
sudo apt update && sudo apt upgrade -y

# Install minimal display environment
sudo apt install --no-install-recommends \
xorg \
openbox \
lightdm \
pulseaudio

# Configure auto-login
sudo nano /etc/lightdm/lightdm.conf
# Add:
[Seat:*]
autologin-user=signage
autologin-user-timeout=0
user-session=openbox

# Create signage user
sudo useradd -m -s /bin/bash signage
sudo usermod -aG audio,video,input signage

Minimal Debian Installation

# Install Debian with "SSH server" and "standard system utilities" only

# Add non-free firmware repository (for hardware support)
sudo nano /etc/apt/sources.list
# Add: non-free-firmware to each line

# Install minimal X environment
sudo apt install --no-install-recommends \
xserver-xorg \
xinit \
openbox \
x11-xserver-utils \
pulseaudio

# Auto-start X on boot
sudo nano /etc/systemd/system/xinit.service

[Unit]
Description=X Server
After=systemd-user-sessions.service

[Service]
ExecStart=/usr/bin/xinit -- :0 vt1
User=signage
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

sudo systemctl enable xinit

Signage Player Software

Browser-Based Kiosk

Chromium Kiosk Mode:

# Install Chromium
sudo apt install chromium-browser

# Create autostart script
mkdir -p ~/.config/openbox
cat > ~/.config/openbox/autostart << 'EOF'
# Disable screen blanking
xset s off
xset -dpms
xset s noblank

# Hide cursor after 5 seconds of inactivity
unclutter -idle 5 &

# Start Chromium in kiosk mode
chromium-browser \
--kiosk \
--noerrdialogs \
--disable-infobars \
--disable-session-crashed-bubble \
--disable-restore-session-state \
--disable-features=TranslateUI \
--check-for-update-interval=31536000 \
--disable-background-networking \
--disable-sync \
--disable-translate \
--disable-extensions \
--disable-default-apps \
--disable-component-update \
--autoplay-policy=no-user-gesture-required \
--start-fullscreen \
--start-maximized \
"https://your-signage-cms.com/display/12345"
EOF

chmod +x ~/.config/openbox/autostart

# Install unclutter for cursor hiding
sudo apt install unclutter

Firefox Kiosk Mode:

# Install Firefox ESR
sudo apt install firefox-esr

# Firefox kiosk mode
firefox-esr --kiosk "https://your-signage-cms.com/display"

# Or with more options via user.js
mkdir -p ~/.mozilla/firefox/signage.default
cat > ~/.mozilla/firefox/signage.default/user.js << 'EOF'
user_pref("browser.cache.disk.enable", true);
user_pref("browser.cache.disk.capacity", 1048576);
user_pref("browser.sessionstore.enabled", false);
user_pref("browser.shell.checkDefaultBrowser", false);
user_pref("browser.tabs.warnOnClose", false);
user_pref("media.autoplay.default", 0);
user_pref("full-screen-api.warning.timeout", 0);
EOF

Native Video Players

VLC for Video Signage:

# Install VLC
sudo apt install vlc

# VLC fullscreen loop playback
cvlc --fullscreen --loop --no-video-title-show \
--video-on-top --no-osd \
--rate 1.0 \
/path/to/playlist.m3u

# VLC with HTTP stream
cvlc --fullscreen --loop \
"http://content-server/stream.m3u8"

# Playlist file (playlist.m3u)
/content/video1.mp4
/content/video2.mp4
/content/video3.mp4

mpv Player (Recommended):

# Install mpv
sudo apt install mpv

# Configuration file ~/.config/mpv/mpv.conf
fullscreen=yes
loop-file=inf
no-osd-bar
no-input-default-bindings
hwdec=auto
vo=gpu
gpu-context=drm # Or x11, wayland depending on setup

# mpv playback command
mpv --fullscreen --loop-file=inf /content/video.mp4

# mpv with playlist
mpv --fullscreen --loop-playlist=inf /content/playlist.m3u

# mpv as systemd service
sudo cat > /etc/systemd/system/signage-player.service << 'EOF'
[Unit]
Description=Signage Video Player
After=graphical.target

[Service]
Environment=DISPLAY=:0
User=signage
ExecStart=/usr/bin/mpv --fullscreen --loop-playlist=inf /content/playlist.m3u
Restart=always
RestartSec=5

[Install]
WantedBy=graphical.target
EOF

Dedicated Signage Software

Xibo for Linux:

# Xibo Player for Linux
# Download from: xibo.org.uk

# Requirements
sudo apt install \
libgtk-3-0 \
libcanberra-gtk3-module \
gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad \
gstreamer1.0-plugins-ugly

# Run Xibo player
./xibo-player

info-beamer:

# Commercial Linux signage solution
# Download from: info-beamer.com

# Features:
# - Hardware accelerated
# - LUA scripting
# - Package-based content
# - Low resource usage

./info-beamer path/to/package

Kiosk Configuration

System Lockdown

# Disable virtual terminals
sudo systemctl mask getty@tty2.service
sudo systemctl mask getty@tty3.service
sudo systemctl mask getty@tty4.service
sudo systemctl mask getty@tty5.service
sudo systemctl mask getty@tty6.service

# Disable Ctrl+Alt+Del
sudo systemctl mask ctrl-alt-del.target

# Disable magic SysRq keys
echo "kernel.sysrq = 0" | sudo tee /etc/sysctl.d/99-disable-sysrq.conf

# Disable USB storage (optional)
echo "blacklist usb-storage" | sudo tee /etc/modprobe.d/disable-usb-storage.conf

# Remove unnecessary packages
sudo apt purge \
nano vim \
wget curl \
ftp telnet \
compilers gcc g++

Read-Only Filesystem

OverlayFS Setup:

# Create initramfs hook for overlayroot
sudo apt install overlayroot

# Configure overlayroot
sudo nano /etc/overlayroot.conf
overlayroot="tmpfs:swap=1,recurse=0"

# Update initramfs
sudo update-initramfs -u

# After reboot, root filesystem is read-only
# Changes are stored in RAM overlay
# Rebooting restores original state

# To make persistent changes:
sudo overlayroot-chroot
# Make changes
exit
sudo reboot

Simpler Approach - Mount Read-Only:

# Edit /etc/fstab
# Change root entry to include 'ro'
/dev/sda1 / ext4 defaults,ro,noatime 0 1

# Add tmpfs for writable areas
tmpfs /tmp tmpfs defaults,noatime,nosuid,size=100m 0 0
tmpfs /var/log tmpfs defaults,noatime,nosuid,size=100m 0 0
tmpfs /var/tmp tmpfs defaults,noatime,nosuid,size=50m 0 0

# Remount scripts
sudo cat > /usr/local/bin/rw << 'EOF'
#!/bin/bash
sudo mount -o remount,rw /
echo "Filesystem is now read-write"
EOF

sudo cat > /usr/local/bin/ro << 'EOF'
#!/bin/bash
sudo mount -o remount,ro /
echo "Filesystem is now read-only"
EOF

sudo chmod +x /usr/local/bin/{rw,ro}

Watchdog Configuration

# Enable hardware watchdog
sudo apt install watchdog

# Configure watchdog
sudo nano /etc/watchdog.conf

watchdog-device = /dev/watchdog
watchdog-timeout = 15
realtime = yes
priority = 1
max-load-1 = 24
ping = 8.8.8.8
interface = eth0
retry-timeout = 60

# Enable and start
sudo systemctl enable watchdog
sudo systemctl start watchdog

Display Configuration

X11 Display Setup

# Configure X11 display
sudo nano /etc/X11/xorg.conf.d/10-display.conf

Section "Monitor"
Identifier "HDMI-1"
Option "DPMS" "false"
EndSection

Section "Screen"
Identifier "Screen0"
Monitor "HDMI-1"
DefaultDepth 24
SubSection "Display"
Depth 24
Modes "1920x1080"
EndSubSection
EndSection

Section "ServerLayout"
Identifier "Layout0"
Screen "Screen0"
EndSection

Section "ServerFlags"
Option "BlankTime" "0"
Option "StandbyTime" "0"
Option "SuspendTime" "0"
Option "OffTime" "0"
EndSection

Multi-Display Setup

# Configure multiple displays
xrandr --output HDMI-1 --mode 1920x1080 --pos 0x0 --primary
xrandr --output HDMI-2 --mode 1920x1080 --pos 1920x0

# Or create xorg.conf for persistent multi-monitor
sudo nano /etc/X11/xorg.conf.d/20-multimonitor.conf

Section "Monitor"
Identifier "HDMI-1"
EndSection

Section "Monitor"
Identifier "HDMI-2"
Option "RightOf" "HDMI-1"
EndSection

# For video wall spanning multiple outputs
# Use nvidia-settings (NVIDIA) or amdcccle (AMD) for advanced config

Display Rotation

# Rotate display 90 degrees (portrait)
xrandr --output HDMI-1 --rotate right

# Rotate options: normal, left, right, inverted

# Permanent rotation in xorg.conf
Section "Monitor"
Identifier "HDMI-1"
Option "Rotate" "right"
EndSection

# For touch input rotation (if applicable)
xinput set-prop "Touch Device" --type=float "Coordinate Transformation Matrix" 0 1 0 -1 0 1 0 0 1

Remote Management

SSH Configuration

# Secure SSH configuration
sudo nano /etc/ssh/sshd_config

Port 22
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
X11Forwarding no
AllowUsers signage-admin

# Restrict SSH to management network
ListenAddress 10.0.0.0

# Generate SSH key pair (on admin machine)
ssh-keygen -t ed25519 -C "signage-admin"
ssh-copy-id -i ~/.ssh/id_ed25519.pub signage-admin@player

Ansible Management

# inventory.yml
all:
children:
signage_players:
hosts:
player-001:
ansible_host: 192.168.1.101
player-002:
ansible_host: 192.168.1.102
player-003:
ansible_host: 192.168.1.103
vars:
ansible_user: signage-admin
ansible_ssh_private_key_file: ~/.ssh/signage_key

# playbook-update.yml
---
- name: Update signage players
hosts: signage_players
become: yes
tasks:
- name: Update apt cache
apt:
update_cache: yes

- name: Upgrade packages
apt:
upgrade: safe

- name: Sync content
synchronize:
src: /content/
dest: /home/signage/content/
delete: yes

- name: Restart signage service
systemd:
name: signage-player
state: restarted

# Run: ansible-playbook -i inventory.yml playbook-update.yml

Monitoring Script

#!/bin/bash
# /usr/local/bin/signage-monitor.sh

HOSTNAME=$(hostname)
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
UPTIME=$(uptime -p)
LOAD=$(cat /proc/loadavg | cut -d' ' -f1-3)
MEMORY=$(free -m | awk 'NR==2{printf "%.1f%%", $3*100/$2}')
DISK=$(df -h / | awk 'NR==2{print $5}')
TEMP=$(cat /sys/class/thermal/thermal_zone0/temp 2>/dev/null | awk '{printf "%.1f°C", $1/1000}')

# Check if X is running
if pgrep -x "Xorg" > /dev/null; then
DISPLAY_STATUS="running"
else
DISPLAY_STATUS="stopped"
fi

# Check if signage app is running
if pgrep -f "chromium.*kiosk" > /dev/null; then
APP_STATUS="running"
elif pgrep -f "mpv" > /dev/null; then
APP_STATUS="running"
else
APP_STATUS="stopped"
fi

# Network check
if ping -c 1 8.8.8.8 &> /dev/null; then
NETWORK_STATUS="connected"
else
NETWORK_STATUS="disconnected"
fi

# Output JSON for monitoring system
cat << EOF
{
"hostname": "$HOSTNAME",
"timestamp": "$TIMESTAMP",
"uptime": "$UPTIME",
"load": "$LOAD",
"memory": "$MEMORY",
"disk": "$DISK",
"temperature": "$TEMP",
"display": "$DISPLAY_STATUS",
"app": "$APP_STATUS",
"network": "$NETWORK_STATUS"
}
EOF

# Post to monitoring endpoint
curl -s -X POST \
-H "Content-Type: application/json" \
-d @- \
https://monitoring.yourcompany.com/api/signage/heartbeat << EOF
{
"hostname": "$HOSTNAME",
"status": "$APP_STATUS",
"load": "$LOAD",
"memory": "$MEMORY"
}
EOF

Security Hardening

Firewall Configuration

# Install and configure UFW
sudo apt install ufw

# Default deny incoming
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow SSH from management network only
sudo ufw allow from 10.0.0.0/24 to any port 22

# Allow signage CMS traffic (adjust ports as needed)
sudo ufw allow out 80/tcp
sudo ufw allow out 443/tcp

# Enable firewall
sudo ufw enable
sudo ufw status verbose

AppArmor Profiles

# Install AppArmor
sudo apt install apparmor apparmor-utils

# Create profile for Chromium
sudo aa-genprof chromium-browser
# Run chromium, exercise all features
# Press S to scan for changes
# Press F to finish

# Enable profile
sudo aa-enforce /etc/apparmor.d/usr.bin.chromium-browser

Automatic Updates

# Install unattended-upgrades
sudo apt install unattended-upgrades

# Configure automatic security updates
sudo nano /etc/apt/apt.conf.d/50unattended-upgrades

Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
};
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:00";

# Enable
sudo dpkg-reconfigure -plow unattended-upgrades

Troubleshooting

Common Issues

No display output:

# Check X log
cat /var/log/Xorg.0.log | grep EE

# Verify display detection
xrandr --query

# Check DRM devices
ls -la /dev/dri/

# Force HDMI output
echo "options drm_kms_helper poll=N" | sudo tee /etc/modprobe.d/drm.conf

Video playback issues:

# Check hardware acceleration
vainfo # VA-API
vdpauinfo # VDPAU

# Install missing codecs
sudo apt install \
gstreamer1.0-plugins-bad \
gstreamer1.0-plugins-ugly \
gstreamer1.0-libav \
ffmpeg

# Check video card driver
lspci -k | grep -A 3 VGA

Network connectivity:

# Check interface status
ip link show

# Check IP configuration
ip addr show

# Test DNS resolution
nslookup your-cms-server.com

# Check routing
ip route show

# Test connectivity
ping -c 4 your-cms-server.com
curl -I https://your-cms-server.com

Frequently Asked Questions


Linux provides a powerful, flexible foundation for digital signage of any scale. For signage CMS software with Linux player support, explore the SignageStudio platform or contact MediaSignage for enterprise solutions.