← Back to DevBytes

Void Linux Server Setup: From Installation to Production

Introduction to Void Linux for Servers

Void Linux is an independent, rolling-release distribution built from scratch — not derived from Debian, Red Hat, or Arch. It distinguishes itself through two core design choices: the XBPS (X Binary Package System) package manager and the runit init system. For server workloads, these choices translate into a lean, fast, and predictable operating system that boots quickly, consumes minimal resources, and avoids the complexity of systemd.

This tutorial walks through a complete Void Linux server setup — from a fresh installation to a hardened, production-ready deployment. By the end, you will have a system capable of hosting web applications, containers, or infrastructure services with confidence.

Why Choose Void Linux for Servers

Key Advantages

When Void Is a Good Fit

Void excels on VPS instances, containers, embedded systems, and any environment where you want tight control over what runs. It is less ideal if you need extensive vendor support, certified compliance, or a vast third-party repository of prebuilt commercial software.

Installation

Obtaining the Installer

Download the appropriate ISO from the official Void Linux website. For servers, the base image (not the XFCE desktop edition) is preferred. Choose the glibc variant unless you have a specific reason to use musl.

# Verify the download integrity
sha256sum void-x86_64-ROOTFS-20231018.tar.xz
# Compare against the SHA256SUMS file from the mirror

Booting and Partitioning

Boot the installer media. The Void installer is a TUI called void-installer. Before launching it, partition your disk manually for full control.

# Identify the target disk
lsblk

# Partition with fdisk or cfdisk
cfdisk /dev/sda

# Example layout:
# /dev/sda1  512M  EFI System  (if UEFI)
# /dev/sda2  4G    Linux swap
# /dev/sda3  rest  Linux filesystem

# Format the partitions
mkfs.fat -F32 /dev/sda1
mkswap /dev/sda2
mkfs.ext4 /dev/sda3

# Activate swap
swapon /dev/sda2

Running the Installer

Launch the installer and follow the prompts:

void-installer

Within the installer, configure the following:

After confirming, the installer downloads and installs the base system. Reboot when complete.

Post-Installation Configuration

First Boot and Updates

Log in as root and immediately bring the system up to date:

# Sync and update all packages
xbps-install -Su

# If a kernel update occurred, reboot
reboot

Installing Essential Packages

# Core utilities for server administration
xbps-install -S sudo vim curl wget git htop tmux \
  openssh chrony rsync ufw fail2ban

# Development and build tools (optional)
xbps-install -S base-devel

Package Management with XBPS

Core Commands

# Search for a package
xbps-query -Rs nginx

# Install a package
xbps-install -S nginx

# Remove a package (keeping config)
xbps-remove nginx

# Remove a package and its dependencies
xbps-remove -R nginx

# List installed packages
xbps-query -l

# Show package information
xbps-query -RS nginx

# Clean the package cache
xbps-remove -O

# Remove orphaned dependencies
xbps-remove -o

Repository Configuration

Void uses signed repositories. The default configuration lives in /usr/share/xbps.d. To override or add repositories, create files in /etc/xbps.d:

# Enable the non-free repository (if needed)
mkdir -p /etc/xbps.d
cp /usr/share/xbps.d/*-repository-*.conf /etc/xbps.d/

# Edit to point to non-free
sed -i 's|repo-default|repo-default/nonfree|' /etc/xbps.d/*-repository-*.conf

# Re-sync
xbps-install -S

Handling Updates Safely

Because Void is rolling, updates should be applied regularly but carefully on production systems:

# Check what would be updated without applying
xbps-install -Sun

# Apply updates
xbps-install -Su

# If the update touches critical packages, sync filesystems and reboot
sync && reboot

Service Management with runit

Understanding the runit Model

Void uses runit as its init and service supervisor. Services live in /etc/sv/, and enabled services are symlinked into /var/service/. Each service is a directory containing at minimum an executable run script.

Enabling and Managing Services

# Enable a service (creates symlink in /var/service)
ln -s /etc/sv/sshd /var/service/

# Disable a service
rm /var/service/sshd

# Check service status
sv status sshd

# Start, stop, restart
sv up sshd
sv down sshd
sv restart sshd

# Check status of all services
sv status /var/service/*

Creating a Custom Service

Suppose you have a Node.js application you want to supervise. Create a service directory:

mkdir -p /etc/sv/myapp

Create the run script:

#!/bin/sh
# /etc/sv/myapp/run

exec 2>&1

cd /opt/myapp
exec chpst -u myappuser:myappuser node server.js

Make it executable and enable it:

chmod +x /etc/sv/myapp/run
ln -s /etc/sv/myapp /var/service/

# Verify it is running
sv status myapp

Logging with runit

runit pairs each service with an optional logging service. Create a log subdirectory:

mkdir -p /etc/sv/myapp/log

Create /etc/sv/myapp/log/run:

#!/bin/sh
exec svlogd /var/log/myapp
mkdir -p /var/log/myapp
chmod +x /etc/sv/myapp/log/run

Now stdout and stderr from your service are captured to /var/log/myapp/current with automatic rotation.

User Management and Sudo

Creating a Non-Root Admin User

# Create a user with a home directory and bash shell
useradd -m -s /bin/bash deploy

# Set a password
passwd deploy

# Add to the wheel group for sudo access
usermod -aG wheel deploy

Configuring Sudo

Uncomment the wheel group in the sudoers file:

visudo

Find and uncomment:

%wheel ALL=(ALL:ALL) ALL

For passwordless sudo (use with caution), use:

%wheel ALL=(ALL:ALL) NOPASSWD: ALL

SSH Hardening

SSH is the primary entry point to your server. Harden it immediately.

Generating SSH Keys

On your local machine, generate a key pair:

ssh-keygen -t ed25519 -C "admin@production"

Copy the public key to the server:

ssh-copy-id deploy@your-server-ip

Securing sshd Configuration

Edit /etc/ssh/sshd_config:

# Disable root login
PermitRootLogin no

# Disable password authentication
PasswordAuthentication no
PubkeyAuthentication yes

# Limit to specific users
AllowUsers deploy

# Change default port (optional but reduces noise)
Port 2222

# Disable empty passwords
PermitEmptyPasswords no

# Limit authentication attempts
MaxAuthTries 3

# Disable X11 forwarding on servers
X11Forwarding no

Restart sshd:

sv restart sshd

Always test your new connection in a separate terminal before closing your existing session.

Firewall Configuration

Using UFW

UFW (Uncomplicated Firewall) provides a simple interface to iptables:

# Default policies
ufw default deny incoming
ufw default allow outgoing

# Allow SSH (use your custom port if changed)
ufw allow 2222/tcp

# Allow HTTP and HTTPS
ufw allow 80/tcp
ufw allow 443/tcp

# Enable the firewall
ufw enable

# Check status
ufw status verbose

Rate Limiting SSH

# Limit SSH to 6 connections per 30 seconds per IP
ufw limit 2222/tcp

Time Synchronization

Accurate time is critical for logs, TLS certificates, and distributed systems. Install and enable chrony:

xbps-install -S chrony
ln -s /etc/sv/chronyd /var/service/

# Verify synchronization
chronyc tracking

Automatic Security Updates

While Void does not ship an automatic updater by default, you can schedule regular update checks with cron or a runit service. A conservative approach is a weekly notification rather than automatic application:

# Install cron
xbps-install -S cronie
ln -s /etc/sv/cronie /var/service/

# Add a weekly update check
cat > /etc/cron.weekly/xbps-check << 'EOF'
#!/bin/sh
UPDATES=$(xbps-install -Sun | wc -l)
if [ "$UPDATES" -gt 0 ]; then
  echo "$UPDATES packages need updating on $(hostname)" | \
    mail -s "Void update notification" admin@example.com
fi
EOF

chmod +x /etc/cron.weekly/xbps-check

Setting Up a Web Server

Installing Nginx

xbps-install -S nginx
ln -s /etc/sv/nginx /var/service/

Basic Site Configuration

Create a server block in /etc/nginx/conf.d/example.com.conf:

server {
    listen 80;
    server_name example.com www.example.com;

    root /var/www/example.com;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }

    access_log /var/log/nginx/example.access.log;
    error_log  /var/log/nginx/example.error.log;
}
# Create the web root
mkdir -p /var/www/example.com
echo "<h1>Hello from Void Linux</h1>" > /var/www/example.com/index.html

# Test and reload
nginx -t
sv restart nginx

Installing Certbot for TLS

xbps-install -S certbot
certbot --nginx -d example.com -d www.example.com

Certbot modifies the Nginx configuration automatically and sets up a renewal cron entry.

Deploying Applications with Docker

# Install Docker
xbps-install -S docker docker-compose

# Enable and start the Docker daemon
ln -s /etc/sv/docker /var/service/

# Add your deploy user to the docker group
usermod -aG docker deploy

# Verify
docker --version
docker run hello-world

System Monitoring and Maintenance

Resource Monitoring

# Real-time process view
htop

# Disk usage
df -h

# Memory usage
free -h

# Network connections
ss -tulnp

# Open files
lsof -i

Log Management

Void logs to /var/log/. The kernel ring buffer is accessible via dmesg. Service logs managed by runit appear in their respective /var/log/<service>/ directories.

# View recent kernel messages
dmesg -T | tail -50

# Follow a service log
tail -f /var/log/nginx/access.log

# View runit-managed logs
cat /var/log/sshd/current

Installing a System Monitor

For lightweight metrics collection, install netdata:

xbps-install -S netdata
ln -s /etc/sv/netdata /var/service/

Access the dashboard at http://your-server-ip:19999. Remember to firewall this port or proxy it behind Nginx with authentication.

Backup Strategy

Configuration Backups

Regularly back up critical directories:

#!/bin/sh
# /usr/local/bin/backup-config.sh

DATE=$(date +%Y%m%d)
tar czf /backups/config-$DATE.tar.gz \
  /etc \
  /var/www \
  /opt/myapp \
  2>/dev/null

# Retain only the last 7 backups
find /backups -name "config-*.tar.gz" -mtime +7 -delete
chmod +x /usr/local/bin/backup-config.sh

# Schedule daily
echo "0 2 * * * /usr/local/bin/backup-config.sh" | crontab -

Database Backups

#!/bin/sh
# /usr/local/bin/backup-postgres.sh

DATE=$(date +%Y%m%d)
PGPASSWORD=$DB_PASSWORD pg_dump -U postgres -Fc mydb > /backups/mydb-$DATE.dump
find /backups -name "mydb-*.dump" -mtime +14 -delete

Best Practices for Production

Security Hardening Checklist

Configuring Fail2ban

xbps-install -S fail2ban
ln -s /etc/sv/fail2ban /var/service/

# Create a local configuration
cat > /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3

[sshd]
enabled = true
port = 2222
logpath = /var/log/sshd/current
EOF

sv restart fail2ban
fail2ban-client status sshd

Kernel and System Tuning

For production workloads, tune kernel parameters in /etc/sysctl.d/99-production.conf:

# Increase file descriptor limits
fs.file-max = 2097152

# Network tuning
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 10000 65535

# Security
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Apply changes
sysctl --system

Raise the open file limit in /etc/security/limits.conf:

* soft nofile 65535
* hard nofile 65535
root soft nofile 65535
root hard nofile 65535

Disabling Unnecessary Services

# List all enabled services
ls -la /var/service/

# Remove anything you do not need
rm /var/service/unused-service

Documentation and Reproducibility

Maintain a provisioning script or configuration management playbook. Even a simple shell script that records every step makes future deployments reproducible:

#!/bin/sh
# provision.sh - Void Linux server provisioning

set -e

xbps-install -Su
xbps-install -S sudo vim curl openssh chrony ufw fail2ban nginx

# Enable services
ln -s /etc/sv/sshd /var/service/
ln -s /etc/sv/chronyd /var/service/
ln -s /etc/sv/nginx /var/service/
ln -s /etc/sv/fail2ban /var/service/

# Firewall
ufw default deny incoming
ufw default allow outgoing
ufw allow 2222/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable

echo "Provisioning complete. Reboot recommended."

Conclusion

Void Linux offers a refreshingly straightforward server platform. Its runit init system makes service management transparent and scriptable, XBPS handles packages with speed and reliability, and the absence of systemd keeps the system lean and understandable. By following this tutorial, you now have a hardened, production-ready Void Linux server with proper user management, SSH hardening, firewall protection, automatic logging, web serving capability, and a documented maintenance routine. The key to long-term stability on a rolling-release distribution is consistency: apply updates regularly, monitor logs proactively, and keep your configuration reproducible through scripts or infrastructure-as-code tooling. With these practices in place, Void Linux provides a dependable foundation for production workloads of nearly any scale.

— Ad —

Google AdSense will appear here after approval

← Back to all articles