Home Server Set-up
Home Server
How to Turn an Old Laptop into a Power-Efficient Proxmox Home Server
Laptops make incredible home lab servers. They consume very little electricity, feature a built-in keyboard and monitor for troubleshooting, and contain an integrated battery backup (UPS) to handle sudden power cuts.
This guide walks you through preparing the hardware, installing Proxmox Virtual Environment (VE), and applying critical laptop-specific tweaks so your node runs reliably 24/7 with the lid closed.
Phase 1: BIOS and Hardware Preparation
Before running the installer, the laptop's motherboard firmware must be configured to support virtualization and prevent boot errors.
- Enter BIOS: Power on the laptop and repeatedly tap the appropriate manufacturer key (e.g., F10 on HP devices) to open the firmware configuration menu.
- Enable Virtualization: Navigate to the Advanced or System Configuration menu and enable Intel Virtualization Technology (VT-x) and VT-d (or AMD-V / AMD-Vi for AMD processors).
- Disable Secure Boot: Turn off Secure Boot to allow the custom Proxmox kernel to bootstrap successfully.
- Set Storage Mode: Ensure the internal storage controller is configured to AHCI mode rather than RAID or Intel RST.
- Connect a Wired Network: Plug a physical Ethernet cable into the laptop's network port or use a compatible USB-to-Ethernet adapter. Proxmox requires a stable, wired connection and does not natively support Wi-Fi configurations during initialization.
Phase 2: Create the Installation Media
- Download the Installer: Visit the official Proxmox website and download the latest Proxmox VE ISO installer image.
- Flash the Drive: Insert a USB flash drive (minimum 8GB) and use a tool like Rufus or BalenaEtcher to burn the downloaded ISO file onto the flash drive.
Phase 3: Run the Proxmox Installation Wizard
- Boot from USB: Insert the flashed USB drive into the laptop, power it on, and repeatedly tap the temporary boot options key (e.g., F9 on HP devices). Select your USB flash drive from the hardware menu.
- Launch Installer: Select Install Proxmox VE (Graphical) from the initial menu choice.
- Select Target Harddisk: Choose your primary internal SSD or HDD. For single-drive laptop builds, format the disk as
ext4orXFS. Avoid ZFS on basic setups to prevent high disk write wear on consumer flash layouts. - Network Configuration:
- Select your active wired Ethernet network interface card.
- Assign a designated Static IP address outside of your home router's automatic DHCP lease pool.
- Input your local gateway router IP and your preferred network DNS server.
- Complete and Reboot: Follow the remaining prompts to set your administrative root password and alert email. Click install, wait for completion, remove the USB drive when prompted, and let the system reboot.
Phase 4: Critical Post-Install Laptop Tweaks & Repository Setup
By default, standard Linux installations are programmed to put laptops to sleep when the lid is closed. Because Proxmox is a headless server, you must explicitly disable this power-saving feature so it operates continuously.
Log into the Proxmox command line (via the physical laptop screen interface or through the Web UI terminal session located at https://<YOUR_ASSIGNED_STATIC_IP>:8006).
Step 1: Disable Laptop Lid Sleep
Open the system configuration file responsible for managing local login behavior and power button triggers:
nano /etc/systemd/logind.conf
Scroll through the file, locate the following lines, and uncomment them by removing the leading hash symbol (#). If they do not exist, append them cleanly to the bottom of the text block:
HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore
HandleLidSwitchDocked=ignore
Step 2: Understand Sleep Blockers (Optional)
Within the same configuration file, you may notice a system entry named LidSwitchIgnoreInhibited.
- Setting this to
=yes(the system default) instructs the laptop to ignore any software process attempting to block power saving, forcing the laptop to sleep anyway on lid close. - Setting this to
=noforces the operating system to respect software processes—such as active virtual machine backups or ongoing disk writes—preventing sleep until those operations complete.
*Note: Because we configured HandleLidSwitch=ignore in Step 1, the operating system is instructed to never enter sleep mode when the physical lid is shut, rendering the secondary inhibitor toggle redundant for this deployment.*
Step 3: Apply and Save Changes
Save the text configuration file and exit the terminal editor (Ctrl + O, tap Enter, then press Ctrl + X). Restart the login management service daemon to instantly initialize your parameters:
systemctl restart systemd-logind
Step 4: Configure the Free No-Subscription Repository
Proxmox comes pre-configured to track paid enterprise update streams. To switch your package index targets to the free community repository so you can safely run updates without authentication errors, run these commands in sequence:
- Disable the Default Enterprise List:
sed -i 's/^deb/#deb/' /etc/apt/sources.list.d/pve-enterprise.list - Add the Clean No-Subscription Mirror Path:
echo "deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription" > /etc/apt/sources.list.d/pve-no-subscription.list - Refresh System Indexes & Apply Security Patches:
apt update && apt dist-upgrade -y
Step 5: Prevent Screen Burn-In
To turn off the physical, built-in laptop display panel automatically after 1 minute of absolute command-line interface inactivity, inject the console-blanking escape sequence straight into the pre-login shell wrapper:
setterm -blank 1 >> /etc/issue
Your power-efficient laptop server is now optimized, secured against lid-close sleep errors, and completely ready to deploy your virtual machines and container stacks!
*************************************************************************************
TigerVNC Server Setup: Auto-Restart on Login
Traditional VNC platforms (like TightVNC) often fail to process modern graphical rendering extensions (like RENDER), causing users to get stuck on an unrendered grey screen. TigerVNC acts as a modern, stable drop-in replacement.
1. Provision the Software Stack
Run the following command to install the standalone server package along with the necessary D-Bus utilities:
sudo apt update
sudo apt install xfce4 xfce4-goodies tigervnc-standalone-server dbus-x11 -y
2. Configure the VNC Environment Initialization Script
Create your custom session startup profile directories and open the deployment file:
mkdir -p ~/.config/tigervnc
nano ~/.config/tigervnc/xstartup
Paste this configuration layout to clean up host environment pollution, add local sandboxing permissions to avoid snap confinement bugs, and start XFCE within a dedicated D-Bus wrapper:
#!/bin/sh
# 1. Clear out host environment pollution leaks
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
# 2. Initialize X resources and structural parameters
[ -r $HOME/.Xresources ] && xrdb $HOME/.Xresources
xsetroot -solid grey
vncconfig -iconic &
# 3. Grant local sandbox permissions (Fixes Snap containment/CCTV viewer bugs)
xhost +local:
# 4. Handle specific execution loops inside a dedicated DBus architecture wrapper
exec dbus-launch --exit-with-session startxfce4
Ensure universal execution permissions are applied to the newly created configuration script:
chmod +x ~/.config/tigervnc/xstartup
3. Initialize Password Authentication Database
Create your system security authentication layer manually:
vncpasswd
Follow the terminal prompts to assign and verify your remote graphical session security password. These authorization keys will be saved directly into your secure directory profile.
4. Construct the Systemd Daemon Layout
To automate performance handling and ensure standard startup execution, map out a system unit config file:
sudo nano /etc/systemd/system/vncserver@.service
Paste the configuration below, substituting server1454 with your active Linux system username. Note the critical incorporation of the -localhost no parameter flag, which forces TigerVNC to bind beyond the loopback domain onto external endpoints like Tailscale network links:
[Unit]
Description=Start TigerVNC server at startup
After=syslog.target network.target
[Service]
Type=forking
User=server1454
WorkingDirectory=/home/server1454
Environment=HOME=/home/server1454
ExecStartPre=-/usr/bin/vncserver -kill :%i > /dev/null 2>&1
ExecStart=/usr/bin/vncserver -depth 16 -geometry 1920x1080 -localhost no :%i
ExecStop=/usr/bin/vncserver -kill :%i
[Install]
WantedBy=multi-user.target
5. Launch and Validate Daemon Services
Register the unit updates, link the profile to runtime boot executions, and initialize the graphical display array:
# Refresh system daemon maps
sudo systemctl daemon-reload
# Bind instance 1 to the autostart framework
sudo systemctl enable vncserver@1.service
sudo systemctl start vncserver@1.service
# Verify networking states across the engine topology
sudo netstat -ptnl | grep vnc
Your system verification matrix must report target network alignment across all active network interfaces, listening globally:
tcp 0 0 0.0.0.0:5901 0.0.0.0:* LISTEN 17714/Xtigervnc
Production Verification & Remote Connection
Launch your preferred client-side visual application toolkit (e.g., Remmina, RealVNC, TigerVNC Viewer) and specify your node's explicit private mapping destination:
[YOUR_SERVER_TAILSCALE_IP]:5901
Once you authenticate with your VNC security password, you will be securely routed through your private network interface and dropped straight into an optimized, lightning-fast XFCE4 desktop session!
*******************************************************************
The Ultimate Guide to Installing, Automating, and Ricing an XFCE Desktop over TigerVNC
XFCE is an incredibly lightweight remote desktop environment, making it the perfect choice for a headless server running a VNC loop. However, the stock configuration can look dated out of the box, and default rendering profiles can cause latency over remote connections.
This guide provides a comprehensive blueprint to deploy a secure, ultra-fast, and modern XFCE desktop environment via TigerVNC on Ubuntu 26.04 LTS, optimized for maximum performance.
Phase 1: Installation & Directory Architecture
First, update your package repository lists and pull down the core XFCE desktop, TigerVNC, and the D-Bus framework wrapper needed for graphical applications to communicate over virtual frames.
sudo apt update
sudo apt install xfce4 xfce4-goodies tigervnc-standalone-server dbus-x11 -y
Next, establish your user-level configuration path mapping:
mkdir -p ~/.config/tigervnc
nano ~/.config/tigervnc/xstartup
Paste this clean initialization sequence. This script flushes stale environmental leaks and uses xhost +local: to prevent Snap application containment blocks:
#!/bin/sh
# 1. Clear out host environment session leaks
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
# 2. Load standard desktop resources
[ -r $HOME/.Xresources ] && xrdb $HOME/.Xresources
xsetroot -solid grey
vncconfig -iconic &
# 3. Grant local sandbox permissions (Fixes Snap/CCTV viewer bugs)
xhost +local:
# 4. Launch XFCE inside a private D-Bus container loop
exec dbus-launch --exit-with-session startxfce4
Save and exit (Ctrl + O, Enter, Ctrl + X). Set correct file system ownership permissions and mark the initialization script as executable:
sudo chown -R server1454:server1454 /home/server1454/.config
chmod +x ~/.config/tigervnc/xstartup
Phase 2: Building the Non-Hanging Systemd Service
To ensure the desktop environment boots automatically on system startup and recovers from power cuts, wire it into a background service.
⚠️ The Pitfall Avoided: We completely removed the standard PIDFile directive from this configuration. The TigerVNC script wrapper often causes systemd to hang indefinitely while waiting for a process ID file that has moved. Removing it forces systemd to track the daemon natively through internal cgroups.
Create the global service configuration file:
sudo nano /etc/systemd/system/vncserver@.service
Paste this optimized configuration block. Note that we have explicitly set the color depth parameter to -depth 16 to reduce data overhead by roughly 33%, and included -localhost no to allow connections across your local network:
[Unit]
Description=Start VNC server at startup
After=syslog.target network.target
[Service]
Type=forking
User=server1454
WorkingDirectory=/home/server1454
Environment=HOME=/home/server1454
ExecStartPre=-/usr/bin/vncserver -kill :%i > /dev/null 2>&1
ExecStart=/usr/bin/vncserver -depth 16 -geometry 1920x1080 -localhost no :%i
ExecStop=/usr/bin/vncserver -kill :%i
[Install]
WantedBy=multi-user.target
Save and close the file. Reload the system management daemon maps, enable the automation layer to run on boot, and start display instance :1:
sudo systemctl daemon-reload
sudo systemctl enable vncserver@1.service
sudo systemctl start vncserver@1.service
Verify that the service has launched successfully and is listening cleanly on port 5901:
sudo systemctl status vncserver@1.service --no-pager
sudo netstat -ptnl | grep vnc
Phase 3: Performance Optimization (The Speed Layer)
VNC transmits updates by packaging modified pixel blocks. To make the interface highly responsive over remote connections, minimize the layout computations on the host server.
- Kill the Window Compositor:
Log into your desktop using a VNC Viewer. Open the main application menu and navigate to Settings → Window Manager Tweaks. Head to the Compositor tab and uncheck Enable display compositing. This cuts out drop shadows and window fade animations, eliminating hundreds of unnecessary network screen redraws. - Switch to a Solid Background:
High-contrast wallpapers require heavy compression cycles over remote desktop protocols. Right-click your desktop wallpaper area, choose Desktop Settings, and set the Style dropdown selection to None. Choose a clean, dark solid color to minimize background transmission overhead.
Phase 4: Modern Visual Ricing (The Aesthetics Layer)
Transform XFCE's default retro interface into a flat, modern workspace by installing the Arc theme suite, Papirus flat icons, and an advanced application search dashboard.
sudo apt update
sudo apt install arc-theme papirus-icon-theme xfce4-whiskermenu-plugin -y
Open your VNC Viewer application and map the newly installed interface assets through your system configuration modules:
| Visual Component | Menu Navigation Path | Configuration to Choose |
|---|---|---|
| Main Window Theme | Settings → Appearance → Style | Arc-Dark |
| System Icon Set | Settings → Appearance → Icons | Papirus-Dark |
| Window Frame Borders | Settings → Window Manager → Style | Arc-Dark |
| Font Smoothing | Settings → Appearance → Fonts | Enable Anti-Aliasing (Hinting: Slight, Sub-pixel: RGB) |
🛠️ Upgrading the Panel Layout
- Deploy the Whisker Launcher: Right-click the stock application launcher menu button on your panel bar and click Remove. Next, right-click any empty spot on the taskbar panel, select Panel → Add New Items, select Whisker Menu, and click Add. Right-click the new button, select Move, and slide it to the far-left corner of the panel frame.
- Reposition the Taskbar: Right-click your panel layout track and open Panel Preferences. Uncheck Lock panel, grab the small dotted selection handles at the edge of the bar, and drag the entire assembly down to the bottom edge of the screen layout. Re-check Lock panel to secure it.
- Optimize Taskbar Scaling: While still inside the panel preferences layout, increase the Row Size (pixels) option to
38. This expands your application shortcuts and status indicators, providing a clean, modern user experience.
How to Enable and Configure SSH on Kali Linux
Secure Shell (SSH) is a critical protocol for administrators and security practitioners, enabling encrypted remote command-line access to a server or workstation. While Kali Linux comes with the OpenSSH server pre-installed in many distribution flavors, the service is disabled by default for security purposes to minimize the host's attack surface.
This guide walks you through installing the underlying software hooks, configuring the background system engine to initialize automatically during boot, and verifying remote cryptographic links safely.
Step 1: Provision the OpenSSH Server
Log into your local Kali Linux instance terminal. Before pulling down packages, it is recommended to update your repository definitions to guarantee compatibility with your rolling-release lifecycle index:
# Refresh package mirrors and install the server binaries
sudo apt update
sudo apt install openssh-server -y
Step 2: Bootstrap and Automate the Service Daemon
Once the package installation wraps up, initialize the background execution loop using systemd. Using the enable directive ensures that the SSH server recovers automatically whenever the system restarts or recovers from power losses:
# Start the runtime service engine instantly
sudo systemctl start ssh
# Bind the process to the system autostart framework
sudo systemctl enable ssh
Verify Operational Daemon States
Confirm that the secure shell listener is active, running, and successfully binding to system network sockets without errors:
sudo systemctl status ssh --no-pager
Look for a solid green active (running) tracking status in your console output block.
Step 3: Establish a Remote Connection
To access your Kali workspace from a separate computer over your network, track down your private local IP address mapping via the terminal:
ip addr show
Once you identify your local network interface address (e.g., 192.168.1.10), launch an SSH terminal or client utility from your remote client machine and target your destination route:
ssh kali@192.168.1.10
🔒 Security Warning: If you are establishing an SSH network link for the first time on a fresh setup, it is highly recommended to change the default user account security password by executing the passwd command inside the terminal window to lock out unauthorized network probes.
Accept the unique ECDSA/RSA cryptographic host key fingerprint verification prompt when prompted, enter your secure user account password credentials, and you will be securely dropped straight into an active remote Kali Linux terminal session!
Enabling VNC Server at Boot and Fixing Common Desktop Errors
Setting up an XFCE or GNOME desktop to launch automatically over a virtual framebuffer at boot is an efficient way to manage a headless server. However, automated systemd startup links can sometimes run into environment locks, audio device blocks, or sandbox containment drops that prevent modern browsers (like Brave or Firefox) from opening inside a VNC session.
This guide walks you through building an optimal automated systemd deployment framework for your VNC server, correcting critical service architecture patterns, and mending common network runtime conflicts.
Step 1: Optimize the VNC Environment Script
First, access your system environment initialization script. This profile configures your virtual display constraints and handles internal message brokers before launching the window manager.
nano ~/.vnc/xstartup
To avoid sandbox access errors (such as the not a snap cgroup warning), your initialization script must explicitly grant local sandbox permissions using xhost and bind to the correct D-Bus system bus path. Update your configuration file to match this structure:
#!/bin/sh
# 1. Clear out host environment session leaks
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
# 2. Load system defaults and resource parameters
[ -r $HOME/.Xresources ] && xrdb $HOME/.Xresources
xsetroot -solid grey
vncconfig -iconic &
# 3. Grant local sandbox permissions (Fixes Snap browser isolation crashes)
xhost +local:
# 4. Map active runtime data tracks and export D-Bus environment vectors
export XKL_XMODMAP_DISABLE=1
export XDG_RUNTIME_DIR="/run/user/$(id -u)"
export DBUS_SESSION_BUS_ADDRESS="unix:path=$XDG_RUNTIME_DIR/bus"
# 5. Launch the desktop environment wrapper cleanly
exec dbus-launch --exit-with-session startxfce4
Save and close out the window, then make sure the initialization script has full executable privileges:
chmod +x ~/.vnc/xstartup
Step 2: Create a Reliable Systemd Daemon Service
To ensure your remote environment starts automatically when the system boots up, manage it through a system service daemon config file:
sudo nano /etc/systemd/system/vncserver@.service
⚠️ The "Hanging Service" Pitfall Addressed: Standard VNC systemd service templates frequently include a PIDFile= directive. This often causes systemd to hang indefinitely or fail on boot because the VNC server script wrapper shifts its process ID file location during initialization. Completely removing the PID tracking property forces systemd to track the core daemon parameters natively via cgroups.
Paste this clean configuration template, replacing ser_g1454 with your active system username:
[Unit]
Description=Start VNC server at startup
After=syslog.target network.target
[Service]
Type=forking
User=ser_g1454
WorkingDirectory=/home/ser_g1454
Environment=HOME=/home/ser_g1454
ExecStartPre=-/usr/bin/vncserver -kill :%i > /dev/null 2>&1
ExecStart=/usr/bin/vncserver -depth 16 -geometry 1920x1080 -localhost no :%i
ExecStop=/usr/bin/vncserver -kill :%i
[Install]
WantedBy=multi-user.target
Step 3: Launch and Enable the Service
Register your new daemon configuration file with the system, enable it to run at boot, and initialize your display socket loop target (:1):
# Force systemd to map the new unit configuration
sudo systemctl daemon-reload
# Enable instance 1 to load during system boot sequences
sudo systemctl enable vncserver@1.service
# Clean up any stale sessions and spin up the service daemon
vncserver -kill :1 > /dev/null 2>&1
sudo systemctl start vncserver@1.service
Verify that your setup is working correctly by checking the system service status logs:
sudo systemctl status vncserver@1.service --no-pager
A successful configuration output will show a clean, green initialization signature:
● vncserver@1.service - Start VNC server at startup
Loaded: loaded (/etc/systemd/system/vncserver@.service; enabled; preset: enabled)
Active: active (running) since Thu 2026-07-09; 10s ago
To confirm that your server engine is actively listening across network interfaces on the correct ports, run a quick network socket query:
sudo netstat -ptnl | grep vnc
Your network verification metrics should show active listener ports matching this profile:
tcp 0 0 0.0.0.0:5901 0.0.0.0:* LISTEN 1669/Xtigervnc
Advanced Lab Troubleshooting Matrix
Error Fix: systemd-networkd-wait-online Timing Out at Boot
If your server is configured to boot completely headless, the startup sequence might hang for up to two minutes while waiting for unlinked interfaces (such as an empty secondary Ethernet port or an unconfigured Wi-Fi card) to establish a connection. You can fix this by restricting the wait script to your primary active interface:
sudo systemctl edit systemd-networkd-wait-online.service
An interactive system override configuration file will open. Insert the tracking constraint line directly between the designated block sections:
[Service]
ExecStart=
ExecStart=/usr/lib/systemd/systemd-networkd-wait-online -i <your_active_interface>
*(Be sure to replace <your_active_interface> with your server's literal network hardware device, such as eno1 or wlp1s0 discovered using the ip a command).*
Error Fix: Audio Core Engines Not Responding inside VNC
If your audio control configurations or software bridges become desynchronized or drop sound channels across virtual connections, execute a clean flush and reinstallation of the sound server packages:
# Purge the core audio engine configurations from your system
sudo apt-get purge alsa-base pulseaudio pavucontrol -y
# Refresh package indices and reinstall core audio packages
sudo apt update
sudo apt install alsa-base pulseaudio pavucontrol -y
# Reboot your server to initialize clean audio channels
sudo reboot
Once your server boots back up, your startup scripts will map correctly, application isolation paths will clear out automatically, and your browser suites will launch cleanly inside your optimized remote desktop session!
Secure, Self-Hosted Remote Desktop: Deploying RustDesk over Tailscale with Docker
Self-hosting your remote desktop infrastructure is a fantastic way to maintain complete control over your data and privacy. RustDesk is a highly capable, open-source alternative to commercial remote desktop software. By running it inside a Docker container and routing it exclusively through a Tailscale VPN mesh, we can create a completely private, encrypted remote access solution.
In this guide, we will deploy the RustDesk ID Server (hbbs) and Relay Server (hbbr), enforce strict encryption keys, and securely retrieve those keys from a distroless container environment.
Step 1: The Docker Compose Configuration
To successfully route RustDesk over Tailscale, your ID server needs to know where to direct clients. We achieve this using the -r flag. Furthermore, to prevent unauthorized connections even within your VPN, we enforce encryption using the -k _ flag on both containers.
Create a new directory for your server, navigate into it, and create your docker-compose.yml file:
services:
hbbs:
container_name: hbbs
image: rustdesk/rustdesk-server:latest
environment:
- ALWAYS_USE_RELAY=Y
# -r points clients to the relay over your VPN. -k _ enforces encryption.
command: hbbs -r <YOUR_VPN_IP>:21117 -k _
volumes:
- ./data:/root
network_mode: "host"
depends_on:
- hbbr
restart: unless-stopped
hbbr:
container_name: hbbr
image: rustdesk/rustdesk-server:latest
# -k _ ensures the relay also enforces encryption using the shared volume key.
command: hbbr -k _
volumes:
- ./data:/root
network_mode: "host"
restart: unless-stopped
Important: Replace <YOUR_VPN_IP> with your server's actual Tailscale/VPN IP address.
Step 2: Deploying the Server
With your compose file configured, spin up the server in the background. Because we are using network_mode: "host", Docker will bind the necessary ports directly to your host network.
docker compose up -d
Step 3: Extracting the Encryption Key
Because we used the -k _ flag, the server automatically generated a highly secure ED25519 key pair on its first boot. You need the public key to configure your client applications.
The Distroless Catch: The official RustDesk Docker image is built on a "distroless" environment. This means standard Linux utilities like cat, ls, and bash are completely removed from the image for security reasons. You cannot simply execute a command inside the container to read the key.
Instead, we use the docker cp trick to extract the file to the host machine's temporary folder, and then read it from there:
- Copy the key out of the container:
sudo docker cp hbbs:/root/id_ed25519.pub /tmp/id_ed25519.pub - Read the copied key:
cat /tmp/id_ed25519.pub
Copy the output text to your clipboard. Treat your keys carefully, but remember that this is the public key (.pub), which is safe to distribute to your personal client devices.
Step 4: Configuring the RustDesk Client
Your server is running and you have your encryption key. Now, it's time to connect a device.
- Open the RustDesk app on your computer or phone.
- Navigate to Settings > Network.
- In the ID Server field, enter your server's Tailscale IP address (e.g.,
100.x.x.x). - In the Relay Server field, enter that exact same IP address again.
- In the Key field, paste the public key string you extracted in Step 3.
- Click Apply.
Return to the main RustDesk dashboard. At the bottom of the screen, you should now see a green "Ready" status. You have successfully deployed a secure, end-to-end encrypted remote administration tool wrapped safely inside your personal VPN mesh!
*******************************************************************
Installing Glances Service and Configuring Hardware Sensors (lm-sensors)
Glances is an advanced, cross-platform system monitoring tool that allows you to track resource utilization via a CLI interface or a built-in web server. By combining Glances with lm-sensors, you can monitor real-time hardware telemetry, including CPU core temperatures, fan speeds, and voltage lines, all from a single centralized interface.
Step 1: Install System Dependencies
Log into your Linux server and update your local package lists, then install the core packages for Glances and the hardware abstractions framework:
sudo apt update
sudo apt install lm-sensors glances -y
Step 2: Detect and Map Hardware Sensors
Before running the software service, initialize your hardware monitoring interface modules. The automated tracking engine will scan your system's motherboard for onboard ISA, PCI, and embedded SMBus monitoring chips:
# Run automated sensor detection probe and bypass interactive prompts
sudo sensors-detect --auto
# Output live hardware metrics to verify functional telemetry
sensors
Step 3: Construct the Glances Systemd Service
To run Glances continually in the background as an automated web server, configure a system service structure:
sudo nano /etc/systemd/system/glances.service
Paste the following service layout template inside the file configuration. This block sets up your resource daemon to listen across all network endpoints (0.0.0.0) on port 61208:
[Unit]
Description=Glances Web Server Daemon
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/glances -w -B 0.0.0.0 -p 61208
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Save and close out the file changes (Ctrl + O, then Enter, followed by Ctrl + X).
Step 4: Initialize and Validate the Service
Force systemd to refresh its internal registry maps, enable the background service to execute at boot, and trigger its runtime instantly using the --now flag:
sudo systemctl daemon-reload
sudo systemctl enable --now glances.service
sudo systemctl status glances.service
💡 Web UI Asset Optimization (Optional Bugfix)
On certain older Debian-derived packages or older Ubuntu iterations, loading the browser connection endpoint can occasionally show an empty container frame. This occurs if the system package structure skips compiling local user assets. To ensure the Web UI components serialize smoothly without access drops, manually confirm the directory mapping path layout exists by running:
sudo mkdir -p /usr/lib/python3/dist-packages/glances/outputs/static/public
Open your browser and navigate directly to your server IP on port 61208 to view your dashboard:
http://<YOUR_SERVER_IP>:61208
Your hardware parameters, RAM configurations, containers, and core thermal limits are now being tracked dynamically on a clean, light page structure!
How to Install Docker on Ubuntu 26.04 LTS (Resolute) — A Step-by-Step Guide
When installing Docker on a fresh modern Linux distribution like Ubuntu 26.04 LTS (resolute), standard copy-paste installation scripts often break. The two most common pitfalls are accidentally pointing the package manager to a Debian repository instead of Ubuntu, or failing to refresh the local package index after updating the sources files.
Here is the complete, error-free walkthrough compiled from our troubleshooting logs to help you successfully deploy Docker on your server from scratch.
Step 1: Prepare System Folders & Download GPG Security Key
Ubuntu requires a signed cryptographic key from Docker to securely verify that the software packages you download haven't been tampered with.
Run these commands to create the secure directory and download the official Ubuntu Docker key:
# Create the secure keyring directory if it doesn't exist
sudo install -m 0755 -d /etc/apt/keyrings
# Download Docker's official GPG encryption key
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
# Grant universal read permissions to the key file
sudo chmod a+r /etc/apt/keyrings/docker.asc
Step 2: Add the Official Ubuntu Docker Repository
Next, tell Ubuntu's package manager (apt) exactly where Docker's official download servers are located. We will write this out using a modern .sources file.
⚠️ The Pitfall to Avoid: Ensure theURIsline points explicitly to/linux/ubuntuand not to/linux/debian.
Execute this block to automatically detect your system architecture and inject the correct codename (resolute):
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
Step 3: Refresh the Local Package Index
Do not skip this step! Your system will not look for the new files until you force an update. Refresh your package definitions map so Ubuntu can see the newly added repository strings:
sudo apt update
While this command runs, watch your terminal logs. You should see a line confirming a successful connection to Docker's servers:
Hit/Get: https://download.docker.com/linux/ubuntu resolute InRelease
Step 4: Install the Complete Docker Suite
With the repository maps correctly linked, you can now install the entire Docker virtualization engine along with its essential plugins:
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
Step 5: Verify the Deployment
Once the background installation and extraction loops wrap up, verify that everything is running perfectly.
1. Check the Background Engine Status
sudo systemctl status docker --no-pager
Look for a solid green active (running) status indicator in the output.
2. Execute a Live Test Container
Force Docker to download and run its standard diagnostic container image:
sudo docker run hello-world
If you see a greeting that reads "Hello from Docker! This message shows that your installation appears to be working correctly," your engine is fully functional.
🚀 Pro-Tip: Run Docker Without Using sudo
By default, running any docker command requires system root privileges. To avoid typing sudo before every single command, add your normal system user to the newly created docker group:
# Add your user to the docker security cgroup
sudo usermod -aG docker $USER
# Apply the group updates to your current terminal session immediately
newgrp docker
You can now run commands like docker ps or docker run directly out of your active terminal window or VNC session!
*******************************************************************
How to Set Up Plex in Docker: From Network Share to Local USB Storage
Setting up Plex Media Server using Docker Compose is one of the most efficient ways to manage your home media. This guide covers how to deploy Plex using either a Network Attached Storage (NAS) share or a locally attached USB drive.
Method 1: Deploying Plex with Network Storage (SMB/CIFS)
If your media library lives on a separate NAS or network share, you can mount it directly inside your Docker container using the local CIFS volume driver loop.
Step 1: Install Host Prerequisites
For Docker to successfully parse network file mounts, the host system must be provisioned with the appropriate filesystem mount helpers. Run the following command on your server terminal:
sudo apt update && sudo apt install cifs-utils -y
Step 2: Create a Secure Environment File
To avoid exposing sensitive login credentials in your main infrastructure files, create a hidden configuration file named .env in your active project directory:
# .env
NAS_USER=your_nas_username
NAS_PASS=your_nas_password
Step 3: Configure Docker Compose
Create a compose.yml file. This setup links Plex directly to the host's network interfaces and structures an isolated network volume map that logs in securely using your environmental parameters:
services:
plex:
image: lscr.io/linuxserver/plex:latest
container_name: plex
network_mode: host
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/London
- VERSION=docker
volumes:
- ./config:/config
- nas_media:/data/nas_shared
restart: unless-stopped
volumes:
nas_media:
driver: local
driver_opts:
type: cifs
o: "username=${NAS_USER},password=${NAS_PASS},iocharset=utf8,vers=3.0"
device: "//YOUR_NAS_IP_ADDRESS/your_share_folder"
Method 2: Deploying Plex with Local USB Storage
If you prefer to attach a physical USB hard drive directly to your server box, the orchestration layout is much simpler. It utilizes a local bind path map, cutting out the need for global volume driver options entirely.
Step 1: Locate the USB Mount Point
Connect your storage device directly to the server framework and identify its active partition assignment structure:
lsblk
Trace your drive through the partition layout tree and note its literal address path under the target mount column (e.g., /mnt/USB/Media).
Step 2: Configure File Security Permissions
Linux environments often restrict mount access profiles by default. Ensure your container engine holds full execution properties across your media library folders:
sudo chmod -R 755 /path/to/your/usb
Step 3: Configure Docker Compose
Populate your configuration deployment file. Notice that the media target maps straight from your raw mount point path down into the internal directory system:
services:
plex:
image: lscr.io/linuxserver/plex:latest
container_name: plex
network_mode: host
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/London
- VERSION=docker
volumes:
- ./config:/config
# Map the local host path directly into the internal container path
- /path/to/your/usb:/data/usb_media
restart: unless-stopped
Final Step: Launch and Add Libraries
- Initialize the background system engine loop:
docker compose up -d - Launch a web browser and navigate directly to the Plex graphical dashboard console:
http://<YOUR_SERVER_IP>:32400/web - Navigate to Add Library, pick your media specification layout (Movies, TV Shows), and click Browse for Media Folder.
- Map the path to your internal directory target:
- Select
/data/nas_sharedif processing network volumes via Method 1. - Select
/data/usb_mediaif running local media drives via Method 2.
- Select
- Save the parameters, and allow Plex to generate metadata files!
Troubleshooting Common Errors
1. Error: "Permission Denied" inside Plex Dashboard
If you connect your library folder profiles inside Plex but directories appear completely blank or display file-locking boundaries, your user definitions are mismatched.
The Fix: Confirm your user variables map directly to your system account ID by executing id in the host shell environment. Then, push comprehensive permissions across your assets recursively:
sudo chmod -R 755 /path/to/your/media
2. Error: Docker fails to mount the NAS volume layout
If the compose architecture throws failure strings when setting up network block loops, the kernel cannot communicate or authenticate against the remote asset target pool.
The Fix: Ensure cifs-utils is correctly installed. Double-check your secure credentials mapping variables. If your storage target uses an older protocol boundary, change your engine version string inside the YAML file from vers=3.0 to vers=2.0.
3. Error: USB drive path mutations after system restarts
If the hardware reboots and Plex completely drops its reference paths to your physical media libraries, your host OS may have auto-mounted the partition block structure to a different folder path naming array.
The Fix: Bind the physical device layout parameters permanently using your host's global file systems table (/etc/fstab) using the device's un-changing unique hardware signature identifier rather than a variable path string. Run the query script to track down your signature tag:
blkid
*************************************************************************************
How to Deploy Pi-hole in Docker: A Step-by-Step Troubleshooting Guide
Setting up a network-wide ad blocker like Pi-hole using Docker is a great weekend project. However, configuration syntax errors and host port conflicts can occasionally pop up during installation.
This guide outlines the exact, step-by-step process to deploy your own Pi-hole instance from scratch while avoiding common pitfalls.
Step 1: Create the Docker Compose Configuration
To get started, you need an orchestration configuration template. Create a directory for your service, then create a file named docker-compose.yml and paste the optimized template below.
This layout enforces correct indentation spacing blocks, ensures your local blocklists survive container restarts, and maps out a explicit password flag variable for your administrative dashboard.
# More info at https://github.com and https://pi-hole.net
services:
pihole:
container_name: pihole
image: pihole/pihole:latest
ports:
- "53:53/tcp"
- "53:53/udp"
- "80:80/tcp"
- "8443:443/tcp" # Changed host port mapping to avoid SSL web conflicts
environment:
TZ: 'Asia/Kolkata'
WEBPASSWORD: 'your_secure_password_here'
FTLCONF_dns_listeningMode: 'ALL'
volumes:
- './etc-pihole:/etc/pihole'
- './etc-dnsmasq.d:/etc/dnsmasq.d'
cap_add:
- SYS_NICE
restart: unless-stopped
Step 2: Handle Port Conflicts (Troubleshooting)
When running docker compose up -d using standard templates, you might encounter a common initialization error stating:
failed to bind host port 0.0.0.0:443/tcp: address already in use
This happens because another web server (like Nginx, Apache, or a reverse proxy) is already utilizing port 443 on your server's primary network interface. You have three ways to bypass this block:
Method A: Change the Host Port (Recommended)
As implemented in our YAML block above, alter the port assignment string on the left side of the colon from "443:443/tcp" to "8443:443/tcp". This transparently maps the container's HTTPS endpoint over an available alternative channel without clashing with host services.
Method B: Stop the Conflicting Service
If your design layout dictates that Pi-hole must hold native port 443, track down the process PID holding that specific socket map by executing:
sudo lsof -i :443
If an unneeded development service (like a placeholder Nginx setup task) is capturing the connection, stop it permanently via systemd:
sudo systemctl stop nginx
Method C: Remove Port 443 Completely
Pi-hole's web dashboard operates natively over HTTP on port 80. If you do not plan on configuring advanced local SSL certificates or specialized HTTPS blockpages for your infrastructure, you can cleanly delete the line - "443:443/tcp" from your docker configuration completely without impact.
Step 3: Launch the Container
Once your network ports are validated and your configuration file is saved, assemble and run your background virtualization stack:
docker compose up -d
The container engine will download the runtime dependencies and initialize the core engine framework. Once initialized, launch a web browser and access your unified dashboard using your custom network mapping destination:
http://<YOUR_SERVER_IP>/admin
Authenticate with the master secret token string you configured on the WEBPASSWORD environment line in Step 1, and you're ready to start pointing your router's DNS settings at your new ad blocker!
*******************************************************************
Self-Hosting Immich: Complete Installation, SMB External Library & Hardware Acceleration Guide
This guide walks through deploying Immich using Docker Compose or Portainer, attaching an external SMB/CIFS Network Attached Storage (NAS) library, troubleshooting database extensions, and configuring AMD/Intel iGPU hardware acceleration for video transcoding and Machine Learning.
1. Directory Structure & Configuration
Create a dedicated deployment directory on your host server:
mkdir -p ~/immich/upload ~/immich/postgres
cd ~/immich
Create an environment file (.env) inside ~/immich/.env:
# File: ~/immich/.env
UPLOAD_LOCATION=/home/your-user/immich/upload
DB_DATA_LOCATION=/home/your-user/immich/postgres
TZ=Asia/Kolkata
IMMICH_VERSION=release
# Database Settings (Alphanumeric characters only, no hyphens or special characters)
DB_PASSWORD=YourStrongPasswordHere123
DB_USERNAME=postgres
DB_DATABASE_NAME=immich
DB_PASSWORD contains only alphanumeric characters (A-Za-z0-9). Special characters or hyphens can cause initialization failures with internal PostgreSQL connection URIs.
2. Docker Compose File
Create the docker-compose.yml file. This configuration uses official vector-supported PostgreSQL images and includes device mappings for hardware-accelerated video transcoding and ML inference.
name: immich
services:
immich-server:
container_name: immich_server
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
devices:
- /dev/dri:/dev/dri # Pass iGPU for VA-API video transcoding
volumes:
- ${UPLOAD_LOCATION}:/data
- /mnt/nas_photos:/usr/src/app/external/nas_photos:ro # SMB Mount
- /etc/localtime:/etc/localtime:ro
env_file:
- .env
ports:
- '2283:2283'
depends_on:
- redis
- database
restart: always
healthcheck:
disable: false
immich-machine-learning:
container_name: immich_machine_learning
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}-openvino
devices:
- /dev/dri:/dev/dri # Pass iGPU for OpenVINO acceleration
volumes:
- model-cache:/cache
env_file:
- .env
restart: always
healthcheck:
disable: false
redis:
container_name: immich_redis
image: valkey/valkey:8-alpine
healthcheck:
test: valkey-cli ping || exit 1
restart: always
database:
container_name: immich_postgres
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_DB: ${DB_DATABASE_NAME}
POSTGRES_INITDB_ARGS: '--data-checksums'
volumes:
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
shm_size: 128mb
restart: always
healthcheck:
disable: false
volumes:
model-cache:
3. Troubleshooting Database Initialization Errors
Error: No vector extension found. Available extensions: vchord, vector
If you encounter vector extension errors, it means the database was initialized with an incompatible PostgreSQL image. Resolve this with a clean database volume reset:
# 1. Stop all containers
docker compose down
# 2. Clear corrupted/older database files
sudo rm -rf ~/immich/postgres
# 3. Ensure correct directory permissions
mkdir -p ~/immich/postgres
sudo chown -R $USER:$USER ~/immich
# 4. Relaunch the stack
docker compose up -d
4. Mounting an SMB/CIFS External Library
To connect an external NAS share (e.g., Samba/Windows Share) as a read-only Immich library:
Step 4.1: Install CIFS Utilities & Create Mount Point
sudo apt update && sudo apt install cifs-utils -y
sudo mkdir -p /mnt/nas_photos
Step 4.2: Create SMB Credentials File
sudo nano /etc/smbcredentials_nas
Add your Samba user credentials:
username=YOUR_SAMBA_USERNAME
password=YOUR_SAMBA_PASSWORD
sudo chmod 600 /etc/smbcredentials_nas
Step 4.3: Configure Automount in /etc/fstab
sudo nano /etc/fstab
Append the following line at the bottom (replace 192.168.x.x with your NAS IP address):
//192.168.x.x/SharedPhotos /mnt/nas_photos cifs credentials=/etc/smbcredentials_nas,uid=1000,gid=1000,dir_mode=0755,file_mode=0644,iocharset=utf8,x-systemd.automount,_netdev 0 0
Mount the share and verify contents:
sudo mount -a
ls -la /mnt/nas_photos
Step 4.4: Verify Read Permissions inside Container
docker exec -it immich_server ls -la /usr/src/app/external/nas_photos
Step 4.5: Add Path in Immich UI
- Log into Immich Web UI at
http://YOUR_SERVER_IP:2283. - Navigate to Administration > External Libraries.
- Click Create Library, select the owner, and save.
- Click the three vertical dots (⋮) on the new library row and select Edit Import Paths.
- Add the in-container path:
/usr/src/app/external/nas_photos - Click ⋮ again and choose Scan All Library Files.
5. Enabling Hardware Acceleration
Video Transcoding (VA-API / QuickSync)
- Verify direct rendering nodes exist on your server:
ls -la /dev/dri - Ensure
devices: - /dev/dri:/dev/driis present underimmich-serverindocker-compose.yml. - In Immich Web UI, go to Administration > Settings > Video Transcoding Settings.
- Set Hardware Acceleration API to VAAPI (for Intel / AMD iGPUs) and save.
Machine Learning (OpenVINO)
Using the -openvino tag on immich-machine-learning utilizes CPU vector instructions (AVX2/AVX-512) and integrated GPUs to accelerate facial recognition and smart search indexing.
docker logs -f immich_machine_learning
6. Dashboard Integration (Homepage)
To display Immich metrics on your Homepage dashboard, generate an API key in Immich (Account Settings > API Keys) and update services.yaml:
- Media & Photos:
- Immich:
icon: immich.svg
href: http://YOUR_SERVER_IP:2283
description: Self-Hosted Photo Management
widget:
type: immich
url: http://YOUR_SERVER_IP:2283
key: YOUR_IMMICH_API_KEY_HERE
fields: ["photos", "videos", "usage"]
*******************************************************************
How to Install Wazuh Security Platform on a Linux Server (Clean Installation)
Wazuh is an enterprise-ready, open-source security monitoring platform that offers unified XDR and SIEM protection. Deploying it on a clean Linux server allows you to aggregate logs, analyze endpoint telemetry, and identify security anomalies seamlessly.
This guide outlines the single-node / all-in-one setup process using the official Wazuh installation assistant. It installs the Wazuh Indexer, Wazuh Server, and Wazuh Dashboard on a single machine.
Minimum Hardware Requirements
Before running the script, ensure your system fulfills the baseline hardware requirements for a single-node setup supporting up to 25 agents:
- CPU: 4 vCPUs
- RAM: 8 GB RAM minimum
- Storage: 50 GB free disk space
- OS: Ubuntu (e.g., 22.04 LTS / 24.04 LTS) or Debian (11 / 12)
Step-by-Step Installation
Step 1: Update the System Dependencies
Log into your target Linux instance and ensure your local package tree and baseline cryptographic tools are up to date.
sudo apt-get update && sudo apt-get upgrade -y
sudo apt-get install -y curl apt-transport-https unzip wget libcap2-bin software-properties-common lsb-release gnupg2
Step 2: Download the Official Installation Assistant
Pull down the official Wazuh installation automated script directly from the secure package repository.
curl -sO https://packages.wazuh.com/4.x/wazuh-install.sh
(Note: If you need a specific version boundary lock, replace 4.x with the target release version string, or run it as shown to grab the current standard lifecycle build).
Step 3: Run the All-In-One Automated Installer
Execute the helper script with the automated single-node payload flag (-a or --all-in-one). This process will build dependencies, compile cryptographic tokens, configure configuration templates, and hook up the background units.
sudo bash wazuh-install.sh -a
Note: The process can take between 5 to 15 minutes depending on your network download speeds and backend processing power.
Post-Installation & Accessing the Dashboard
Once the installation workflow achieves a successful exit status, the terminal output will print a confirmation block detailing your local node routes:
Wazuh indexer installation finished successfully.
Wazuh server installation finished successfully.
Wazuh dashboard installation finished successfully.
Installation finished.
You can access the web interface at https://<YOUR_SERVER_IP>
User: admin
Password: <A_RANDOMLY_GENERATED_SECURE_PASSWORD>
Step 4: Secure Your Generated Credentials
- Copy the randomly generated password immediately from the terminal window.
- Save this token inside an encrypted credential store or password manager.
- Do not clear the terminal session until this token is logged safely.
Step 5: Log in to the Web Interface
- Launch a browser window and navigate to your server's endpoint:
https://<YOUR_SERVER_IP> - Because the automated script provisions secure, self-signed local TLS certificates, your browser will display a warning stating that the connection is not private.
- Click on Advanced and choose Proceed or Accept the Risk.
- Enter the default master user profile name (
admin) along with the custom generated password sequence you captured in Step 4.
Verifying Server Services Status
If you ever need to manually track or confirm the core operational health states of the system daemons via CLI, run these diagnostic status strings directly over SSH:
-
Verify Indexer Backend Status:
sudo systemctl status wazuh-indexer -
Verify Core Management Engine Status:
sudo systemctl status wazuh-manager -
Verify Web Console UI Engine Status:
sudo systemctl status wazuh-dashboard
Your clean Wazuh environment is now fully deployed and ready to ingest telemetry agents!
*******************************************************************
How to Configure the Grafana OpenSearch Data Source for Wazuh Alerts
Connecting Grafana to your Wazuh Indexer allows you to build rich, custom security dashboards using your security telemetry. Because the Wazuh Indexer is built on OpenSearch architecture, using the official Grafana OpenSearch plugin provides the most reliable connection and field mapping.
Follow this complete step-by-step blueprint to install the plugin, bypass common UI validation errors, and properly map your security log fields without exposing sensitive credentials.
Step 1: Check and Install the OpenSearch Plugin
Before configuring the database connection, ensure Grafana has the translation layer required to speak to an OpenSearch backend.
- Check your Grafana UI under Connections > Data sources. Search for OpenSearch.
- If it is not listed, log into your Grafana server's terminal and deploy it securely via the Grafana CLI framework:
grafana-cli plugins install grafana-opensearch-datasource - Restart your local systems daemon to apply the plugin update immediately:
sudo systemctl restart grafana-server
Step 2: Configure the Primary Connection Settings
Navigate back to your Grafana web interface under Connections > Data sources > Add new data source, select OpenSearch, and apply these specific core settings:
- Name:
wazuh-opensearch-core(or any descriptive cluster identifier). - URL: Enter your indexer network endpoint (e.g.,
https://<YOUR_INDEXER_IP>:9200).
*(Note: If Grafana runs inside a Docker container on the same host machine, usehttps://host.docker.internal:9200to resolve the network link loopback).* - Basic Auth: Toggle this ON. Under Basic Auth Details, enter your secure cluster service account credentials.
- Skip TLS Verify: Toggle this ON. This parameter is mandatory if your indexer targets utilize default self-signed SSL certificates, which Grafana will otherwise reject.
Step 3: Input Index Mappings & Bypass the UI Validation Bug
A known UI validation bug in the plugin frequently throws a "Data source not found" or "404" error if you attempt to fetch the server version before a primary database record exists. Use this exact workflow to bypass it:
- Scroll down to the OpenSearch details block form.
- Index name: Enter
wazuh-alerts-4.x-*(this targets the default daily security logs index pattern). - Time field name: Type
@timestamp. - Version (The Bypass): Do not click "Get Version and Save". Instead, manually click inside the empty version text block and explicitly hardcode a placeholder version value like
2.13.0. - Clear Unsaved States: Ensure the Logs metadata boxes (
Message field nameandLevel field name) and the Data Links arrays are completely empty for this initial setup write. If a placeholder container block has initialized, use the trash can icon to clear it. - Save the Shell: Scroll to the absolute bottom of the configuration console page and click the green Save & test button.
- Refresh: As soon as Grafana attempts the initial write loop, manually reload your browser window (F5) to force the client interface to parse the newly registered data source UID correctly.
Step 4: Finalize Log Definitions & Verify
Now that the structural data source profile is registered inside Grafana's core database architecture, you can safely map your database elements without triggering web dashboard validation crashes.
- Re-open your newly saved OpenSearch data source connection profile.
- Scroll down directly to the Logs sub-section form.
- Message field name: Enter
rule.description. This mapping directive tells Grafana which field string contains the human-readable event alert notification text. - Level field name: Enter
rule.level.
💡 Pro-Tip: Avoid utilizing generic server configurations likesyslog_level. Wazuh passes alert severities as clean numerical arrays ranking from 0 to 15 inside therule.levelkey, which Grafana's Explore UI parses cleanly for automated dashboard color-coding.
Scroll down to the footer link and click Save & test one final time. You will receive a green confirmation success badge reading:
Index OK. Time field name OK.
Your Grafana analytics pipeline is now completely deployed, structured correctly, and ready for you to build rich security visualizations!
*************************************************************************************
How to Monitor Docker Containers with Wazuh
A step-by-step guide to configuring the native Wazuh Docker Listener module for real-time container tracking and event logging.
docker-listener wodle to monitor container lifecycle events (start, stop, pause, pull). This configuration works for both a standalone Wazuh Agent and a local Wazuh Manager host listening to /var/run/docker.sock.
Step 1: Install Python Docker SDK
The Docker listener module requires the Python Docker library to interact directly with the local Docker API socket.
# Ubuntu / Debian
sudo apt update && sudo apt install python3-pip python3-docker -y
# RHEL / AlmaLinux / CentOS
sudo yum install python3-pip -y
sudo pip3 install docker
Step 2: Grant Socket Permissions
Ensure the Wazuh service account holds sufficient read and write permissions to communicate across the Docker Unix socket.
# Add the wazuh system user to the docker group
sudo usermod -aG docker wazuh
Step 3: Enable the Docker Listener Module
Open the main configuration file on your target host:
sudo nano /var/ossec/etc/ossec.conf
Add the <wodle name="docker-listener"> block inside the main <ossec_config> tag:
<wodle name="docker-listener">
<disabled>no</disabled>
<interval>10m</interval>
<attempts>5</attempts>
<run_on_start>yes</run_on_start>
</wodle>
Step 4: Restart Service & Verify Logs
Restart the active daemon (Manager or Agent) to initialize the module:
# For Wazuh Manager setup
sudo systemctl restart wazuh-manager
# For Wazuh Agent setup
sudo systemctl restart wazuh-agent
Stream the log file to confirm the background process initialized successfully:
sudo tail -n 30 /var/ossec/logs/ossec.log | grep -i docker
Step 5: Testing & Dashboard Verification
- Run a test container on the host machine to generate an event:
docker run --rm hello-world
- Open your Wazuh Dashboard web interface.
- Navigate to Modules > Docker or query Explore > Discover using the rule filter:
rule.groups: docker
*******************************************************************
Automated Debian & Kali Linux Daily Diagnostics Script
Running manual health audits on system components, software repositories, virtualization runtimes, and local network configurations can become tedious. By consolidating everyday tracking tools into a unified maintenance shell utility, you can audit your system parameters, verify server socket mappings, check disk health, and clean up unneeded dependencies in a single run.
This guide presents a comprehensive, production-ready system maintenance script designed for server configurations and Kali Linux environments, followed by an operational breakdown of each metric captured.
The Complete Maintenance Script (dailyscri.sh)
Save the following script framework to your local machine. It runs core package management streams, probes active network sockets, queries storage allocations, and manages hardware states automatically.
#!/bin/bash
# ==============================================================================
# DEBIAN / KALI LINUX DAILY MAINTENANCE & DIAGNOSTIC UTILITY
# ==============================================================================
# Usage: sudo ./dailyscri.sh
# Ensure runtime execution permissions are granted: chmod +x dailyscri.sh
# Ensure script executes with root authorization
if [ "$EUID" -ne 0 ]; then
echo "[-] Error: Please run this diagnostic script with sudo privileges."
exit 1
fi
echo "======================================================================"
echo " 🔄 PHASE 1: PACKAGE MANAGER RESYNCHRONIZATION & REPAIR"
echo "======================================================================"
echo "[+] Initializing local package index refresh..."
sudo apt update
echo "----------------------------------------------------------------------"
echo "[+] Executing upstream distribution upgrade patches..."
sudo apt upgrade -y
echo "----------------------------------------------------------------------"
echo "[+] Validating software tree integrity & fixing broken states..."
sudo apt --fix-broken install -y
echo "----------------------------------------------------------------------"
echo "[+] Purging residual isolated dependencies and software locks..."
sudo apt autoremove -y
echo ""
echo "======================================================================"
echo " 📡 PHASE 2: NETWORK INTERFACES & SECURE TOPOLOGY"
echo "======================================================================"
echo "[+] Fetching active NordVPN Meshnet peer distributions..."
if command -v nordvpn &> /dev/null; then
sudo nordvpn meshnet peer list
else
echo "[-] NordVPN binary not active on this node framework."
fi
echo "----------------------------------------------------------------------"
echo "[+] Auditing firewall profile configurations..."
# sudo ufw verbose
echo "----------------------------------------------------------------------"
echo "[+] Interrogating graphical network VNC socket bindings..."
sudo netstat -ptnl | grep vnc
echo "----------------------------------------------------------------------"
echo "[+] Listing local network socket listener interfaces..."
# sudo netstat -tulpn
echo "----------------------------------------------------------------------"
echo "[+] Querying structural link layer IP routing tables..."
ip addr
echo ""
echo "======================================================================"
echo " ⚙️ PHASE 3: OS ARCHITECTURE & ENVIRONMENT IDENTIFICATION"
echo "======================================================================"
echo "[+] Querying platform hostname mapping specifications..."
sudo hostnamectl
echo "----------------------------------------------------------------------"
echo "[+] Extracting LSB distribution release parameters..."
sudo lsb_release -a
echo "----------------------------------------------------------------------"
echo "[+] Reading base kernel release parameters..."
cat /etc/os-release
echo ""
echo "======================================================================"
echo " 📊 PHASE 4: DISK MANAGEMENT & CONTAINER METRICS"
echo "======================================================================"
echo "[+] Querying Samba network storage connection states..."
if command -v smbstatus &> /dev/null; then
sudo smbstatus --shares | column -t
else
echo "[-] Samba tracking utility not accessible."
fi
echo "----------------------------------------------------------------------"
echo "[+] Isolating active container virtualization footprints..."
# sudo docker ps
echo "----------------------------------------------------------------------"
echo "[+] Mapping block storage partitions & USB mount tracks..."
lsblk
echo ""
echo "======================================================================"
echo " 🛠️ PHASE 5: HARDWARE FOOTPRINT & PERFORMANCE METRICS"
echo "======================================================================"
echo "[+] Analyzing CPU core instruction topologies..."
lscpu
echo "----------------------------------------------------------------------"
echo "[+] Reading system kernel compilation parameters..."
uname -a
echo "----------------------------------------------------------------------"
echo "[+] Generating short-form hardware component configurations..."
# hwinfo --short | column -t
echo "----------------------------------------------------------------------"
echo "[+] Sampling kernel virtual memory and scheduling operations..."
vmstat
echo "----------------------------------------------------------------------"
echo "[+] Tracking active open file descriptors and port leaks..."
# sudo lsof -i -P -n
echo ""
echo "======================================================================"
echo " 🔌 PHASE 6: SERVICE MANAGEMENT & PERIPHERAL AUTOMATION"
echo "======================================================================"
echo "[+] Checking vulnerability scanner engine daemon states (GVM)..."
# sudo gvm-start
echo "----------------------------------------------------------------------"
echo "[+] Resetting Bluetooth subsystem communication states..."
sudo systemctl restart bluetooth.service
sudo systemctl daemon-reload
echo "----------------------------------------------------------------------"
echo "[+] Cycling network radio interfaces to force fresh lease hooks..."
sudo nmcli radio wifi off
sudo nmcli radio wifi on
echo "======================================================================"
echo " ✅ SYSTEM DIAGNOSTICS LOG LOOP COMPLETE!"
echo "======================================================================"
Script Operational Breakdown
This automated maintenance tool groups standalone management commands into specialized diagnostic categories:
| Command String | Functional Tracking Target |
|---|---|
nordvpn meshnet peer list |
Lists remote machine pathways, routing variables, and unique public keys associated with your private encrypted mesh network topology. |
netstat -ptnl | grep vnc |
Audits the active network stack to confirm your graphical TigerVNC/TightVNC daemon engines are listening securely on port 5901. |
smbstatus --shares |
Monitors active cross-platform Samba network file shares, tracking client mounts and active locking mechanics. |
vmstat |
Outputs low-overhead performance statistics, covering process queues, memory paging activity, disk block operations, and CPU cycles. |
apt --fix-broken install |
Unlocks the apt package manager if incomplete extractions or broken maintainer dependencies occur during updates. |
nmcli radio wifi off / on |
Cycles the local wireless radio frequencies via NetworkManager to flush stale connection flags and force a fresh DHCP IP address lease request. |
How to Deploy and Schedule the Utility
To implement this script, paste the block code into a local text file, make it executable, and run it with administrative privileges:
# Create the script file
nano dailyscri.sh
# Apply executable permissions
chmod +x dailyscri.sh
# Execute the diagnostic framework
sudo ./dailyscri.sh
To completely automate your system maintenance, you can schedule this script to run as a root crontab task. Open the system scheduler by running sudo crontab -e and append a cron expression—such as 0 2 * * * /home/yourusername/dailyscri.sh—to run the entire audit process automatically every night at 2:00 AM completely hands-free.
How to Build an Automated Daily Server Diagnostic Dashboard
Managing a Linux server requires constant vigilance. Between tracking package updates, monitoring disk health, auditing network ports, and reviewing SSH access logs, system administration can quickly become overwhelming.
In this guide, we will build a completely automated, zero-cost observability stack. We will create a core diagnostic script to gather system health metrics, wrap it in a secondary engine to highlight daily changes, and serve it to a sleek web dashboard using a lightweight Python server.
Step 1: The Core Diagnostic Script
First, we need a script that gathers all the raw data. This script uses enterprise-grade diagnostics to check your packages, network maps, Docker containers, and security logs.
- Create the script file in your home directory:
nano ~/dailyscri.sh - Paste the following optimized code block. (Note: We use
apt-getwith the-qqflag to prevent CLI interface warnings during automated runs).
#!/bin/bash
# ==============================================================================
# SYSTEM MAINTENANCE, MONITORING & DIAGNOSTIC AUTOMATION SCRIPT
# ==============================================================================
if [ "$EUID" -ne 0 ]; then
echo "[-] Error: This script must be run with sudo."
exit 1
fi
echo "======================================================================"
echo "🚀 STARTING COMPREHENSIVE SYSTEM AUDIT PACK"
echo "======================================================================"
echo "Timestamp: $(date)"
echo ""
# --- PHASE 1: PACKAGE MANAGEMENT & SOFTWARE UPDATES ---
echo "======================================================================"
echo "🔄 PHASE 1: PACKAGE MANAGEMENT & SOFTWARE UPDATES"
echo "======================================================================"
apt-get update -qq
apt-get full-upgrade -y -qq
apt-get --fix-broken install -y -qq
apt-get autoremove -y -qq
if command -v snap &> /dev/null; then
snap refresh &> /dev/null
fi
echo "[+] Package updates and cleanup complete."
echo ""
# --- PHASE 2: OS ENVIRONMENT & HOST IDENTIFICATION ---
echo "======================================================================"
echo "📋 PHASE 2: OS ENVIRONMENT & HOST IDENTIFICATION"
echo "======================================================================"
hostnamectl | grep -E "Static hostname|Operating System|Kernel"
echo ""
# --- PHASE 3: HARDWARE & STORAGE TOPOLOGIES ---
echo "======================================================================"
echo "💻 PHASE 3: HARDWARE & STORAGE TOPOLOGIES"
echo "======================================================================"
echo "[+] Disk Capacity (df -h):"
df -h | grep -E "^/dev/"
echo ""
echo "[+] Physical Disk S.M.A.R.T. Status:"
if command -v smartctl &> /dev/null; then
smartctl -H /dev/sda | grep "test result" || echo "[-] Cannot read /dev/sda SMART status."
else
echo "[-] smartmontools not installed."
fi
echo ""
# --- PHASE 4: NETWORK ARCHITECTURE ---
echo "======================================================================"
echo "📡 PHASE 4: NETWORK ARCHITECTURE"
echo "======================================================================"
echo "[+] Local IP Addresses:"
ip -br addr
echo ""
echo "[+] Active Listening Ports (TCP/UDP):"
netstat -tulpn | grep LISTEN
echo ""
if command -v tailscale &> /dev/null; then
echo "[+] Tailscale Status:"
tailscale status
echo ""
fi
# --- PHASE 5: CONTAINER & DAEMON HEALTH ---
echo "======================================================================"
echo "📊 PHASE 5: CONTAINER & DAEMON HEALTH"
echo "======================================================================"
echo "[+] Failed Systemd Services:"
systemctl --failed
echo ""
if command -v docker &> /dev/null; then
echo "[+] Docker Container Resource Usage:"
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}"
echo ""
fi
# --- PHASE 6: SECURITY & ACCESS AUDITING ---
echo "======================================================================"
echo "🛡️ PHASE 6: SECURITY & ACCESS AUDITING"
echo "======================================================================"
echo "[+] Last 5 Successful SSH Logins:"
journalctl -u ssh.service --no-pager | grep "Accepted" | tail -n 5 || echo "[-] No recent remote logins found."
echo ""
echo "[+] Critical System Errors Since Last Boot:"
journalctl -p 3 -xb --no-pager | tail -n 10
echo ""
if command -v fail2ban-client &> /dev/null; then
echo "[+] Fail2Ban SSH Blocks:"
fail2ban-client status sshd
echo ""
fi
echo "======================================================================"
echo "✅ SYSTEM AUDIT COMPLETE"
echo "======================================================================"
Save the file and grant it execution permissions:
chmod +x ~/dailyscri.sh
Step 2: The Diffing & Web Output Wrapper
Next, we create a "wrapper" script. This script executes the diagnostic tool, compares today's data against yesterday's run (to highlight new ports or changes), and packages it all into an HTML file.
nano ~/run_daily_report.sh
Paste the following logic, ensuring you replace YOUR_USERNAME with your actual Linux user directory name:
#!/bin/bash
# ==============================================================================
# REPORT GENERATOR & DIFFING ENGINE
# ==============================================================================
# Define Paths
REPORTS_DIR="/home/YOUR_USERNAME/reports"
WEB_DIR="/home/YOUR_USERNAME/web_dashboard"
DIAG_SCRIPT="/home/YOUR_USERNAME/dailyscri.sh"
# 1. Ensure directories exist
mkdir -p "$REPORTS_DIR"
mkdir -p "$WEB_DIR"
# Ensure a dummy "yesterday.txt" exists for the very first run
if [ ! -f "$REPORTS_DIR/yesterday.txt" ]; then
touch "$REPORTS_DIR/yesterday.txt"
fi
# 2. Run the diagnostic script and save today's output
sudo "$DIAG_SCRIPT" > "$REPORTS_DIR/today.txt"
# 3. Compare Today vs Yesterday (Ignoring dynamic timestamps)
diff -u "$REPORTS_DIR/yesterday.txt" "$REPORTS_DIR/today.txt" | grep -v "Timestamp:" > "$REPORTS_DIR/changes.txt"
# 4. Generate the HTML Report
HTML_FILE="$WEB_DIR/index.html"
cat << 'EOF' > "$HTML_FILE"
<!DOCTYPE html>
<html>
<head>
<title>Daily Server Report</title>
<style>
body { font-family: monospace; background-color: #1e1e1e; color: #d4d4d4; padding: 20px; }
h1 { color: #569cd6; border-bottom: 1px solid #333; padding-bottom: 10px; }
h2 { color: #4ec9b0; margin-top: 30px; }
pre { background-color: #252526; padding: 15px; border-radius: 5px; overflow-x: auto; }
.added { color: #608b4e; } /* Green for new lines */
.removed { color: #d16969; } /* Red for removed lines */
</style>
</head>
<body>
<h1>Daily Server Diagnostics</h1>
<p>Report generated on: $(date)</p>
<h2>What Changed Since Yesterday? (The Diff)</h2>
<pre>
EOF
# Read the diff file and add basic syntax highlighting
while IFS= read -r line; do
if [[ $line == +* ]] && [[ $line != +++* ]]; then
echo "<span class=\"added\">$line</span>" >> "$HTML_FILE"
elif [[ $line == -* ]] && [[ $line != ---* ]]; then
echo "<span class=\"removed\">$line</span>" >> "$HTML_FILE"
else
echo "$line" >> "$HTML_FILE"
fi
done < "$REPORTS_DIR/changes.txt"
cat << 'EOF' >> "$HTML_FILE"
</pre>
<h2>Full Report (Today)</h2>
<pre>
EOF
cat "$REPORTS_DIR/today.txt" >> "$HTML_FILE"
cat << 'EOF' >> "$HTML_FILE"
</pre>
</body>
</html>
EOF
# 5. Rotate the files for tomorrow
mv "$REPORTS_DIR/today.txt" "$REPORTS_DIR/yesterday.txt"
echo "Report generated successfully at $HTML_FILE"
Save the file and grant it execution permissions:
chmod +x ~/run_daily_report.sh
Step 3: Serve the Dashboard & Automate
We will use Python's built-in HTTP module to serve this directory securely over your local network without needing a heavy Nginx or Apache setup.
- Generate the first report: Run the wrapper script manually to create the directories and initial HTML file.
sudo ~/run_daily_report.sh - Start the Web Server: Navigate to the web folder and launch the Python server in the background using
nohup.cd ~/web_dashboard nohup python3 -m http.server 8080 &(You will see a message like
[1] 2959278 nohup: ignoring input.... Press Enter to return to your prompt. The server is now running permanently in the background). - Automate with Cron: Tell your system to run the report every morning at 2:00 AM.
sudo crontab -ePaste this line at the absolute bottom of the file (adjusting your username):
0 2 * * * /home/YOUR_USERNAME/run_daily_report.sh
🎉 Mission Complete! You can now open a browser on your local network and navigate to http://<YOUR_SERVER_IP>:8080. Every morning, you will wake up to a fresh dashboard detailing exactly what changed on your server overnight, complete with green and red highlights for added or removed system states!
*******************************************************************
Automating a Global Homelab: Intelligent Updates, Telegram Alerts, and Conditional Reboots
Managing a distributed fleet of servers—like remote virtual machines, local Raspberry Pis, and core Proxmox hypervisors—presents a unique automation challenge. You want all nodes to update securely and reliably, but you can't treat them all the same. For example, standard nodes should reboot after an update, but your core hypervisor must stay awake to protect running VMs. Furthermore, some nodes might have custom web dashboards that need triggering, while others just need standard package updates.
In this guide, we will build a "Master Orchestrator" bash script. This single, universal script dynamically adapts to the machine it is running on, pushes success/failure alerts directly to your phone via Telegram, and safely manages the reboot sequence.
Step 1: Set Up Telegram Alerts
Before writing the script, we need a way for the servers to securely message you. Telegram's API is incredibly fast, reliable, and free.
- Open Telegram and search for @BotFather (look for the verified checkmark).
- Send the message
/newbot, give it a name, and choose a unique username ending in "bot". - BotFather will provide an HTTP API Token. Save this securely.
- Start a chat with your new bot and send it a quick "hello".
- In your computer's web browser, go to:
https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates - Look for the section that says
"chat":{"id":123456789...and copy that number. This is your personal Chat ID.
Step 2: The Master Orchestrator Script
Connect to your server via SSH and create the automation script in the root directory:
sudo nano /root/auto_update.sh
Paste the following script. Be sure to replace the placeholder Telegram credentials at the top, and modify the "pve" hostname to match whatever you named your Proxmox server.
#!/bin/bash
# ==============================================================================
# MASTER AUTOMATION: REPORTING, ALERTS & REBOOTS (TELEGRAM EDITION)
# ==============================================================================
# Telegram Credentials
TELEGRAM_TOKEN="YOUR_BOT_TOKEN_HERE"
TELEGRAM_CHAT_ID="YOUR_CHAT_ID_HERE"
MACHINE_NAME=$(hostname)
# Function to send the Telegram message silently
send_telegram() {
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendMessage" \
-d "chat_id=${TELEGRAM_CHAT_ID}" \
-d "text=$1" \
-d "parse_mode=Markdown" > /dev/null
}
# ------------------------------------------------------------------------------
# 1. INTELLIGENT UPDATE & DIAGNOSTIC EXECUTION
# ------------------------------------------------------------------------------
# Dynamically search for a custom web dashboard script (if you have one)
REPORT_SCRIPT=$(find /home -maxdepth 2 -name "run_daily_report.sh" 2>/dev/null | head -n 1)
if [ -n "$REPORT_SCRIPT" ] && [ -f "$REPORT_SCRIPT" ]; then
# If a custom dashboard script exists on this specific server, execute it
"$REPORT_SCRIPT"
if [ $? -eq 0 ]; then
UPDATE_STATUS="✅ Web Dashboard, Diagnostics & Updates successful"
else
UPDATE_STATUS="❌ Web Dashboard Execution FAILED"
fi
else
# Fallback for standard servers without custom dashboards
apt-get update -qq
apt-get full-upgrade -y -qq
apt-get autoremove -y -qq
if [ $? -eq 0 ]; then
UPDATE_STATUS="✅ Standard Updates successful"
else
UPDATE_STATUS="❌ Standard Updates FAILED"
fi
fi
# ------------------------------------------------------------------------------
# 2. ALERTS & REBOOT HANDLING
# ------------------------------------------------------------------------------
if [ "$MACHINE_NAME" == "pve" ]; then
# PROXMOX EXCEPTION: Send alert, but DO NOT reboot
MESSAGE="🖥️ *${MACHINE_NAME}* ($UPDATE_STATUS). Skipping reboot to protect running VMs."
send_telegram "$MESSAGE"
exit 0
else
# ALL OTHER SERVERS: Send alert and gracefully reboot
MESSAGE="🔄 *${MACHINE_NAME}* ($UPDATE_STATUS). Server is now rebooting."
send_telegram "$MESSAGE"
# Wait 5 seconds to ensure the webhook sends, then reboot
sleep 5
/sbin/reboot
fi
Save the file and grant it execution permissions:
sudo chmod +x /root/auto_update.sh
Step 3: Staggered Cron Scheduling
If you tell all your servers to update at the exact same minute, they will compete for network bandwidth. Worse, a VM might try to update at the same moment its host hypervisor is updating. Staggering is key.
Open the root crontab on each server:
sudo crontab -e
Important: If you previously had independent update or dashboard scripts scheduled in cron, delete them now. This Master Script handles everything!
Assign a unique time to each node in your fleet. For example:
- VM 1:
0 3 * * * /root/auto_update.sh(3:00 AM) - Raspberry Pi:
30 3 * * * /root/auto_update.sh(3:30 AM) - Remote Bare Metal:
0 4 * * * /root/auto_update.sh(4:00 AM) - Proxmox Host:
30 4 * * * /root/auto_update.sh(4:30 AM)
Step 4: Manual Testing & Verification
To verify the setup without waiting for 3:00 AM, run the script manually as root:
sudo /root/auto_update.sh
Depending on the machine, you will experience the script's built-in intelligence. Standard nodes will text you and gracefully drop your SSH connection as they reboot. However, when you test this on your Proxmox server, your phone will buzz with the message: "Skipping reboot to protect running VMs," and your terminal session will remain completely intact.
You now have a fully automated, zero-touch infrastructure. Enjoy waking up to a fully patched server fleet and a neat row of green checkmarks on your phone every morning!
*******************************************************************
Benchmarking and Diagnosing Linux DNS Performance
Here is your comprehensive blog guide mapping out the DNS diagnostics workflow, followed by a production-ready, single-file Bash script to automate the entire assessment.
When web applications face intermittent lag or loading stalls, network administrators frequently ignore the most common failure point: the Domain Name System (DNS) resolver layer. Slow DNS lookup speeds introduce silent latencies that mirror hard network drops. This guide outlines a structured approach to inspecting, testing, and optimizing your Linux server's DNS layout.
Step 1: Map the Active Upstream Topology
Before modifying system files, you must isolate exactly where your system directs its outbound routing requests. Modern Linux distros delegate this execution loop to the local systemd-resolved interface layer.
resolvectl status | grep "DNS Servers" -A 2
🔍 Evaluating Results: This exposes your direct link layer targets. If you see a private local gateway (like 192.168.1.1), your lookups are bottlenecked by consumer-grade router hardware before hitting the public internet.
Step 2: Measure Baseline Lookup Latency
Using dig (Domain Information Groper), you can pinpoint the turnaround time of a live transaction down to a single millisecond.
dig ubuntu.com
Scan the resulting text matrix for the explicit Query time signature block:
- 0 ms – 4 ms: Reading directly out of local system cache memory.
- 5 ms – 30 ms: Ideal performance. Connected to a highly responsive, physically localized tier-1 resolver.
- 31 ms – 80 ms: Standard performance metrics for traditional ISP or localized cloud nodes.
- > 120 ms: Poor performance. Your current upstream server is saturated or suffering from high geographical path latency.
Step 3: Test Real-Time Stability and Variance
A single lightning-fast resolution does not guarantee stability. Network configurations must remain resilient under repetitive querying loops without dropping traffic packets or experiencing timing spikes.
while true; do dig google.com | grep "Query time"; sleep 1; done
📊 Jitter Analysis: Watch for large millisecond swings. If your latency jumps from 15ms up to 250ms under sequential lookups, your upstream provider is throttling requests or suffering from packet corruption. Press Ctrl + C to terminate the loop.
Step 4: Benchmark and Identify Fast Alternatives
If baseline checks show high latencies, bypass your system’s defaults entirely to query optimized public anycast infrastructure directly. This tells you if your performance issues are local or provider-based.
dig google.com @1.1.1.1 | grep "Query time"
By forcing dig to use Cloudflare’s @1.1.1.1 endpoint, you establish an optimized baseline. If this public lookup outpaces your local system defaults, your server's primary network configuration files should be updated to use public providers permanently.
Automated DNS Diagnostic Script
Save the following code block as a single executable script file (e.g., dns_audit.sh) on your server. It completely automates the steps detailed above, checking dependencies, calculating local performance, and running live external benchmarks.
#!/bin/bash
# ==============================================================================
# Linux DNS Performance & Diagnostic Benchmarking Tool
# ==============================================================================
# Ensure environment has diagnostic utilities present
if ! command -v dig &> /dev/null; then
echo "⚙️ Missing dependency 'dnsutils' detected. Attempting automated install..."
sudo apt update -qq && sudo apt install dnsutils -y -qq
fi
clear
echo "======================================================================"
echo " 🔍 PHASE 1: ACTIVE SYSTEM RESOLVER TOPOLOGY"
echo "======================================================================"
if command -v resolvectl &> /dev/null; then
ACTIVE_DNS=$(resolvectl status | grep "DNS Servers" -A 2)
if [ -n "$ACTIVE_DNS" ]; then
echo "$ACTIVE_DNS"
else
echo "⚠️ No upstream records found in resolvectl status."
fi
else
echo "Fallback Mode (/etc/resolv.conf):"
grep nameserver /etc/resolv.conf
fi
echo ""
echo "======================================================================"
echo " 📊 PHASE 2: BASELINE RESPONSE MATRIX"
echo "======================================================================"
echo "Executing cold query against target node (ubuntu.com)..."
dig ubuntu.com | grep -E "Query time|SERVER"
echo ""
echo "======================================================================"
echo " 📈 PHASE 3: STABILITY & JITTER ASSESSMENT (5-CYCLE STRESS TEST)"
echo "======================================================================"
for i in {1..5}; do
TIME_MS=$(dig google.com | grep "Query time" | awk '{print $4}')
echo " -> Query Iteration $i: \${TIME_MS} ms"
sleep 0.5
done
echo ""
echo "======================================================================"
echo " ⚡ PHASE 4: ANYCAST PUBLIC RESOLVER BENCHMARK"
echo "======================================================================"
echo "System Default Link: \$(dig google.com | grep "Query time" | awk '{print \$4, \$5}')"
echo "Cloudflare Platform: \$(dig google.com @1.1.1.1 | grep "Query time" | awk '{print \$4, \$5}')"
echo "Google Engine Domain: \$(dig google.com @8.8.8.8 | grep "Query time" | awk '{print \$4, \$5}')"
echo "Quad9 Secure Node: \$(dig google.com @9.9.9.9 | grep "Query time" | awk '{print \$4, \$5}')"
echo "======================================================================"
echo "💡 Optimization Rule: If public platforms outpace your System Link"
echo " by > 20ms, update your system interface to reference them permanently."
How to use the script:
- Copy the code block above into a new file on your server:
nano dns_audit.sh - Apply executable privileges to the shell file:
chmod +x dns_audit.sh - Execute the automated auditing matrix:
./dns_audit.sh
*******************************************************************
Automating Homelab Uptime: 60-Second Watchdog Alerts & Dashboards
When running a distributed fleet of servers, you need to know the moment a node drops offline. However, there is a fundamental logical problem: a server cannot send an alert if it is dead or disconnected.
To solve this, we can designate one highly available machine (like a core hypervisor that rarely reboots) to act as a "Watchdog." In this guide, we will set up a lightweight bash script to actively ping your network every 60 seconds and push Telegram alerts on state changes. Then, we will visualize this telemetry data in Home Assistant for a beautiful, real-time command center.
Part 1: The Watchdog Script (Telegram Alerts)
Log into your most stable "always-on" server as root and create the Watchdog script. This script remembers the previous state of your servers, ensuring you only receive alerts when a server drops offline or comes back online (preventing notification spam).
sudo nano /root/watchdog.sh
Paste the following code, replacing the placeholder Telegram credentials and IP addresses:
#!/bin/bash
# ==============================================================================
# SERVER FLEET WATCHDOG (UPTIME MONITOR)
# ==============================================================================
TELEGRAM_TOKEN="YOUR_BOT_TOKEN_HERE"
TELEGRAM_CHAT_ID="YOUR_CHAT_ID_HERE"
# Define your servers in a format: "Name|IP_Address"
# (Exclude the server running this script)
SERVERS=(
"Remote-Server|100.x.x.x"
"Local-VM|100.x.x.x"
"Raspberry-Pi|100.x.x.x"
)
# Directory to store the UP/DOWN state of each server
STATE_DIR="/tmp/watchdog_states"
mkdir -p "$STATE_DIR"
# Function to send Telegram alerts silently
send_telegram() {
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendMessage" \
-d "chat_id=${TELEGRAM_CHAT_ID}" \
-d "text=$1" \
-d "parse_mode=Markdown" > /dev/null
}
# Loop through each server and check its status
for SERVER in "${SERVERS[@]}"; do
NAME="${SERVER%%|*}"
IP="${SERVER##*|}"
STATE_FILE="$STATE_DIR/${NAME}.state"
# If this is the first time running, assume the server is UP
if [ ! -f "$STATE_FILE" ]; then
echo "UP" > "$STATE_FILE"
fi
LAST_STATE=$(cat "$STATE_FILE")
# Ping the server (send 3 packets, timeout after 3 seconds)
if ping -c 3 -W 3 "$IP" > /dev/null 2>&1; then
CURRENT_STATE="UP"
else
CURRENT_STATE="DOWN"
fi
# Compare current state to last known state. If it changed, send an alert!
if [ "$CURRENT_STATE" != "$LAST_STATE" ]; then
if [ "$CURRENT_STATE" == "DOWN" ]; then
send_telegram "🚨 *OFFLINE ALERT:* Server **${NAME}** is unreachable!"
else
send_telegram "✅ *RECOVERY:* Server **${NAME}** is back ONLINE!"
fi
# Save the new state
echo "$CURRENT_STATE" > "$STATE_FILE"
fi
done
Save the file and grant it execution permissions:
sudo chmod +x /root/watchdog.sh
Next, automate the script using Cron so it checks your fleet every 60 seconds. Open your crontab (sudo crontab -e) and add this line to the bottom:
* * * * * /root/watchdog.sh
Part 2: Visualizing Health in Home Assistant
While the bash script handles push notifications, we want a visual dashboard. Home Assistant has a native, code-free integration specifically for this.
- In Home Assistant, navigate to Settings > Devices & Services.
- Click Add Integration and search for Ping (ICMP).
- Enter the IP addresses of your servers. Home Assistant will automatically generate a binary sensor (UP/DOWN) and a numeric sensor (Latency/Ping in ms) for each node.
Building the Dashboard Cards
Edit your Home Assistant dashboard and add the following cards to create a comprehensive Network Operations Center (NOC) view:
-
The Uptime List (Entities Card): Add an Entities card and select the
binary_sensorfor each machine. This gives you a clean list showing "Connected" or "Disconnected" at a glance. -
The Outage Timeline (History Graph Card): Add a History Graph card using the same
binary_sensorentities. Set the "Hours to show" to 24. This generates a color-coded bar chart where green means UP, making it easy to spot overnight network drops. -
Real-time Network Speed (Gauge Card): Add a Gauge card using the
sensor.xxx_round_trip_time_averageentity. Enable severity colors (e.g., Green for 0ms, Yellow for 150ms, Red for 300ms) to monitor network lag and congestion in real-time.
With these two systems running in tandem, you achieve the best of both worlds: silent telemetry logging with visual dashboards when you want to look, and aggressive Telegram push notifications when things actually break.
*******************************************************************
Official Ubuntu Repository URLs for sources.list and DEB822 Formats
The package management system in Ubuntu (apt) relies on a central configuration mapping file to track down and pull down binary software updates. If your configuration files become corrupted during an upgrade or an aggressive repository mirror test, you can completely restore your tracking definitions by matching your specific system lifecycle profile below.
⚠️ Architecture Notice: Starting with Ubuntu 24.04 LTS, the operating system has officially transitioned away from the traditional, single-linesources.listfile configuration format into a structured, multi-line container framework known as the DEB822 standard format located inside/etc/apt/sources.list.d/ubuntu.sources.
Ubuntu 24.04 LTS "Noble Numbat" (Modern DEB822 Format)
For modern environments running Noble Numbat, populate your configuration paths inside the modern file layer layout at /etc/apt/sources.list.d/ubuntu.sources:
# Core Applications, Updates, Backports, and Proposed Pools
Types: deb deb-src
URIs: http://us.archive.ubuntu.com/ubuntu/
Suites: noble noble-updates noble-backports noble-proposed
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
# Mainstream Security Baseline Patches
Types: deb deb-src
URIs: http://security.ubuntu.com/ubuntu/
Suites: noble-security
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
Ubuntu 22.04 LTS "Jammy Jellyfish" (Traditional One-Line Format)
For long-term hardware systems using Jammy Jellyfish, parameters remain mapped via the traditional single-string array layout profile. Append or replace this layout schema within the core file container located at /etc/apt/sources.list:
deb http://archive.ubuntu.com/ubuntu/ jammy main restricted universe multiverse
# deb-src http://archive.ubuntu.com/ubuntu/ jammy main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu/ jammy-updates main restricted universe multiverse
# deb-src http://archive.ubuntu.com/ubuntu/ jammy-updates main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu/ jammy-security main restricted universe multiverse
# deb-src http://archive.ubuntu.com/ubuntu/ jammy-security main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu/ jammy-backports main restricted universe multiverse
# deb-src http://archive.ubuntu.com/ubuntu/ jammy-backports main restricted universe multiverse
deb http://archive.canonical.com/ubuntu/ jammy partner
# deb-src http://archive.canonical.com/ubuntu/ jammy partner
*(Note: The hash symbols # on the deb-src parameters safely comment out source-code repository targets, cutting out redundant download cycles if your server environment only tracks standard pre-compiled binaries).*
Understanding Ubuntu Repository Components
Both layout configuration types share the same standard repository component classifications. These classifications split package groups based on licensing rules and support lifecycles:
| Component Naming | Functional Parameters & Software Scopes |
|---|---|
| main | Officially supported, completely free, open-source software maintained directly by the core Canonical engineering teams. |
| restricted | Proprietary driver modules, microcode binaries, and critical infrastructure dependencies required for hardware functionality that are not fully open-source. |
| universe | A massive ecosystem of community-maintained free and open-source packages, managed entirely by volunteer community developers. |
| multiverse | Software packages restricted by patents, copyright protections, or legal constraints (e.g., specific media codecs), requiring manual user compliance checks. |
Step 3: Flush and Reinitialize Cache Pools
Once you have restored your destination maps to their appropriate structure, execute a full metadata cache purge via your terminal to drop corrupted indexes and pull down a clean mirror list:
# Purge active database repository paths
sudo rm -rf /var/lib/apt/lists/*
# Run verification indexes alignment update
sudo apt update
Your package manager is now cleanly aligned with official repository nodes and ready to resume normal software operations!
How to Manage and Fix the Ubuntu Display Manager (GDM)
The display manager is the graphical interface responsible for user authentication and launching your Ubuntu desktop environment (the login screen). If it breaks or goes inactive, your system will fail to load the desktop, often leaving you stuck on a black screen or a blinking cursor.
This step-by-step guide covers how to check your display manager, switch between alternatives, and fix the system if the gdm service fails to start.
Part 1: Managing Your Display Manager
Step 1: Check Which Display Manager is Running
To see what display manager your system is currently utilizing, open your terminal and run:
systemctl status display-manager
Common active options include: gdm3 (Default Ubuntu GNOME), lightdm (Lightweight alternative), or sddm (KDE Plasma).
Step 2: Switch to a Different Display Manager
If you have multiple display managers installed and want to swap between them, reconfigure the active package allocation array:
sudo dpkg-reconfigure gdm3
*(Note: Running this reconfiguration command on whichever manager is active will open up the exact same selection engine list).*
- A interactive configuration screen will open in your terminal. Use your arrow keys to select your preferred default manager and press Enter.
- Restart your computer to apply the visual changes.
Part 2: Troubleshooting an Inactive GDM Service
If your desktop won't load because GDM is inactive, immediately press Ctrl + Alt + F2 (or F3) on your keyboard to drop into a text-only TTY virtual terminal. Log in with your standard username and password, then execute the following fixes in order:
Fix 1: Force Enable and Start the Service
Sometimes GDM is simply turned off or disabled after a desktop environment modification. You can force it to run and ensure it persists across system reboots:
sudo systemctl enable gdm3 --now
Fix 2: Verify the Boot Target is Graphical
If your operating system configurations accidentally got modified to boot only into a text-only CLI mode, tell systemd to load the graphical desktop interface framework by default:
sudo systemctl set-default graphical.target
Fix 3: Clear Out Full Disk Space
GDM will silently crash and refuse to start if your storage drive has exhausted its capacity to write lockfiles or session metrics. Check your disk space status:
df -h
If the root file system partition (/) is at 100% capacity, free up space instantly by clearing cached system installation archives:
sudo apt clean
sudo apt autoremove
Fix 4: Purge and Reinstall GDM
If binary files or environment definitions became corrupted during a partial system upgrade, perform a clean reinstallation of the display components:
sudo apt update
sudo apt purge gdm3 -y
sudo apt install gdm3 ubuntu-desktop -y
Select gdm3 on the terminal interactive pop-up menu if prompted during this compilation phase.
Fix 5: Turn Off Wayland (For NVIDIA Users)
Proprietary NVIDIA graphics drivers frequently conflict with modern Wayland display protocols, crashing GDM before it can spin up. Revert to the older, stable Xorg standard instead:
- Open the core configuration display file:
sudo nano /etc/gdm3/custom.conf - Locate the commented line:
#WaylandEnable=false - Delete the leading hash symbol (
#) to uncomment the string parameter. - Save the changes (Ctrl + O, then Enter) and exit (Ctrl + X).
Final Step: Reboot
Once you have applied these standard fixes, restart your server platform to load your clean graphical login desktop environment:
sudo reboot
*************************************************************************************
HP All-In-One Ubuntu Server Display & Power Automation Guide
Running Linux on an All-In-One hardware setup or standalone server frame often presents a unique challenge: the integrated LCD monitor remains permanently illuminated even when operating completely headless or through an SSH session. This guide provides an automated shell utility to detect your exact kernel backlight hardware channels, map persistent control shortcuts, and deploy system cron schedules to manage your screen power states seamlessly.
Step 1: The Automated Power Configuration Script
This single-file shell utility automatically discovers your GPU interface panel directory under the sysfs framework, calculates the absolute panel limits, maps shell commands for your environment, and configures an energy automation lifecycle.
#!/bin/bash
# ==============================================================================
# HP ALL-IN-ONE UBUNTU SERVER DISPLAY CONFIGURATOR
# ==============================================================================
# Ensure script runs as root
if [ "$EUID" -ne 0 ]; then
echo "[-] Please run this script with sudo: sudo ./setup_screen.sh"
exit 1
fi
echo "[+] Starting automated display configuration..."
# 1. Detect Backlight Interface
BACKLIGHT_DIR="/sys/class/backlight"
INTERFACE=$(ls "$BACKLIGHT_DIR" | head -n 1)
if [ -z "$INTERFACE" ]; then
echo "[-] Error: No backlight controller found in $BACKLIGHT_DIR"
exit 1
fi
BACKLIGHT_PATH="$BACKLIGHT_DIR/$INTERFACE"
echo "[+] Detected backlight interface: $INTERFACE"
# 2. Get Max Brightness
MAX_BRIGHTNESS=$(cat "$BACKLIGHT_PATH/max_brightness")
echo "[+] Detected maximum panel brightness value: $MAX_BRIGHTNESS"
# 3. Configure Time Zone (Defaulting to Asia/Kolkata)
TARGET_TZ="Asia/Kolkata"
echo "[+] Setting system time zone to $TARGET_TZ..."
timedatectl set-timezone "$TARGET_TZ"
echo "[+] Time zone successfully updated to: $(timedatectl | grep 'Time zone')"
# 4. Inject Bash Aliases for the Calling User
REAL_USER=${SUDO_USER:-$USER}
REAL_USER_HOME=$(eval echo ~$REAL_USER)
BASHRC_PATH="$REAL_USER_HOME/.bashrc"
if [ -f "$BASHRC_PATH" ]; then
echo "[+] Injecting shortcuts into $BASHRC_PATH..."
# Remove existing matching aliases to prevent duplicate entries
sed -i '/alias screenoff=/d' "$BASHRC_PATH"
sed -i '/alias screenon=/d' "$BASHRC_PATH"
# Append new aliases
echo "alias screenoff='echo 0 | sudo tee $BACKLIGHT_PATH/brightness'" >> "$BASHRC_PATH"
echo "alias screenon='echo \$(cat $BACKLIGHT_PATH/max_brightness) | sudo tee $BACKLIGHT_PATH/brightness'" >> "$BASHRC_PATH"
# Fix ownership of the modified .bashrc file
chown "$REAL_USER":"$REAL_USER" "$BASHRC_PATH"
echo "[+] Shortcuts 'screenoff' and 'screenon' configured successfully."
else
echo "[-] Warning: Could not locate .bashrc for user $REAL_USER. Skipping aliases."
fi
# 5. Configure Console Idle Blanking (1 minute)
echo "[+] Setting local console timeout to 1 minute..."
setterm --blank 1 --term linux < /dev/tty1 2>/dev/null || true
# 6. Install Root Cron Jobs for Automation
echo "[+] Scheduling daily power automation (Off at 22:00, On at 07:00)..."
# Build temporary crontab file
TMP_CRON=$(mktemp)
# Export existing cron jobs to avoid losing them
crontab -l > "$TMP_CRON" 2>/dev/null || true
# Strip out older brightness cron jobs to avoid duplicate entries
sed -i '\#/sys/class/backlight/#d' "$TMP_CRON"
# Append the new time rules using variables detected in step 1 & 2
echo "0 22 * * * echo 0 > $BACKLIGHT_PATH/brightness" >> "$TMP_CRON"
echo "0 7 * * * cat $BACKLIGHT_PATH/max_brightness > $BACKLIGHT_PATH/brightness" >> "$TMP_CRON"
# Apply the new crontab rules
crontab "$TMP_CRON"
rm "$TMP_CRON"
echo "[+] Automated cron jobs deployed successfully."
echo "=============================================================================="
echo "[+] SETUP COMPLETE!"
echo "[*] To activate your new manual commands immediately, run: source ~/.bashrc"
echo "[*] You can now use 'screenoff' and 'screenon' freely!"
echo "=============================================================================="
Step 2: Deployment & Initializing Commands
To execute this orchestration setup on your host framework, write the script text out to a local file, grant execution privileges, and run the configuration engine wrapper using administrative access:
# Create the script file framework
nano setup_screen.sh
# Apply binary runtime permissions
chmod +x setup_screen.sh
# Fire the automation process loop
sudo ./setup_screen.sh
# Source your profile to dynamically unlock your manual shortcuts
source ~/.bashrc
Step 3: Execution Logs & Verification Matrix
Upon clean execution, the setup module will track system state parameters and output detailed system validation logs matching this tracking profile:
[+] Starting automated display configuration...
[+] Detected backlight interface: intel_backlight
[+] Detected maximum panel brightness value: 937
[+] Setting system time zone to Asia/Kolkata...
[+] Time zone successfully updated to: Time zone: Asia/Kolkata (IST, +0530)
[+] Injecting shortcuts into /home/server1454/.bashrc...
[+] Shortcuts 'screenoff' and 'screenon' configured successfully.
[+] Setting local console timeout to 1 minute...
[+] Scheduling daily power automation (Off at 22:00, On at 07:00)...
[+] Automated cron jobs deployed successfully.
==============================================================================
[+] SETUP COMPLETE!
[*] To activate your new manual commands immediately, run: source ~/.bashrc
[*] You can now use 'screenoff' and 'screenon' freely!
==============================================================================
Step 4: Configure Automatic Hardware Idle Timeout (Optional)
To drop hardware energy expenditure automatically when you step away from the physical workspace, tie your hardware cutoffs straight to the native Linux terminal utility setterm. This forces the server frame to toggle an standard screen blanking configuration if there is 1 minute of complete local keyboard inactivity on the direct tty channel:
sudo bash -c 'setterm --blank 1 --term linux < /dev/tty1'
Once active, your shortcuts handle direct runtime checks, and your background engine manages overnight maintenance tasks automatically, saving power and extending the hardware life of your panel environment completely hands-free.
*******************************************************************
Comments
Post a Comment