Saltar a contenido

Devuan Server Installation Guide

Target: PC / Workstation (AMD Ryzen processor and NVIDIA GPU)

Filesystem: ext4

Desktop: None. Terminal / SSH

Init: sysvinit

Drive: NVMe SSD (/dev/nvme0n1)

The Devuan Wiki requires sign-up, however, official installation instructions are available at: devuan.org/os/documentation/install-guides/excalibur/install-devuan

Installation is straightforward using the netinstall option.

Before you begin:

- Ensure BIOS is set to UEFI (not legacy) - Enable virtualization (SVM/VT-x) - Disable secure boot (for NVIDIA drivers)


1. Download Devuan and Create a Bootable ISO

Download Devuan Excalibur from files.devuan.org/

  • Choose devuan_excalibur (latest stable release)
  • Choose installer_iso
  • Download the _netinstall ISO

Create a bootable ISO with USB. PulsarTECH has a great Ventoy + Linux multi-boot USB guide available at: youtube.com/watch?v=BfjLJ0CqWsY

On Linux:

sudo dd if=devuan_excalibur_6.1.0_amd64_netinstall.iso of=/dev/sdX bs=4M status=progress conv=fsync

2. Boot the Live ISO

Boot the PC from USB. Here's a reference list of Boot Menu keys for various manufacturers:

Manufacturer Boot Menu Key BIOS/UEFI Key
Acer F12, Esc, F9 F2, Delete
Asus F8, Esc F2, Delete
Dell F12 F2
HP F9, Esc F10
Lenovo F12, F10, F8 (or Novo Button) F2, F1
MSI F11 Delete
Toshiba F12 F2, F1, Esc
Samsung F12, F2, Esc F2
Sony VAIO F11, F10, Esc (or Assist Button) F2, F1, F3
Gigabyte F12 Delete, F2
Intel NUC F10 F2

3. Install Devuan

Once at the Devuan GUI screen, choose the Install option:

  1. Language, Location, Keyboard
    • Select your system language, location, and keyboard layout.
  2. Network Configuration
    • The installer will automatically configure the network if connected via ethernet. If using WiFi, provide the SSID and passphrase for your network.
    • Enter a Hostname. Example: devuanserver (any name is fine, as long as there are no spaces or special characters)
    • Leave the Domain name blank (unless you want to add)
  3. Root Password and User Account
    • Set a Root password
    • Add a user account. Example: firstname
    • Set a password for the user
  4. Clock and Timezone
    • Enter your time zone. Example: Eastern
  5. Disk Partitioning
    • If setting up encryption (optional), follow these instructions for partitioning disks: devuan.org/os/documentation/install-guides/excalibur/full-disk-encryption.html
    • If not setting up encryption (recommended for a simplified install), you can select Guided - use entire disk or Manual
    • If you select Manual, Partition Layouts are provided below based on preference (Simple Partition Layout recommended)
    • After creating the partitions, write the changes to disks - select <Yes>
  6. Package Manager Configuration
    • Select a Devuan archive mirror. Preferred choice is deb.devuan.org
    • If you need to use a HTTP proxy, you can enter the proxy info. Otherwise, just leave blank and select <Continue>
  7. Popularity-Contest Configuration
    • Participating in the package usage survey is optional. Select <Yes> or <No> based on preference (<No> recommended)
  8. Software Selection
    1. Use the spacebar key to deselect everything then select only SSH server and standard system utilities. Do not select any desktop environment. This produces a minimal headless server install
  9. Init System Selection
    • Options for init are:
      • sysvinit (default, classic, well-documented)
      • OpenRC (modern, dependency-based, popular in Gentoo)
      • runit (minimalist, fast boot)
    • For default init (most stable and compatible), select sysvinit
    • If you prefer a more modern and dependency-based init system (that's closer to systemd in usage), choose openrc
  10. Boot Loader
    • Install GRUB to the EFI partition. When asked to install GRUB boot loader to primary drive, select <Yes>
    • Select /dev/nvme0n1 as the device for the boot loader installation
  11. Finish and Reboot
    • The installation is now complete. Select <Continue> to reboot
    • Remove the USB drive immediately after rebooting

# Mount Size Filesystem Purpose
1 /boot/efi 1GB FAT32 Required for UEFI booting
2 /boot 2GB ext4 OS partition to simplify recovery
3 / Remaining ext4 Data storage

Design Rationale

  • Keep the server simple
  • Reduce fragmentation and issues with partition sizes
  • Optimize for running Docker and containerized apps
  • Using NAS for bulk storage to offload media

If you prefer an enterprise-level and isolated partition style, you can opt for:

Enterprise Partition Layout

# Mount Point Size Filesystem Purpose
1 /boot/efi 512 MiB FAT32 (EFI System Partition) UEFI bootloader
2 /boot 2 GiB ext4 Kernel and initramfs images
3 / 50 GiB ext4 Root filesystem
4 /var 100 GiB ext4 Logs, databases, container layers, package cache
5 /tmp 10 GiB ext4 (mounted noexec,nosuid,nodev) Temporary files
6 swap 32 GiB Linux swap Swap space (roughly 1/3 of max RAM)
7 /home 50 GiB ext4 User home directories
8 /srv Remainder ext4 or XFS Server data, VMs, containers, datasets

Design Rationale

  • Separate /var: Runaway logs or container image accumulation cannot fill the root filesystem and crash the system.
  • Separate /tmp: Mounted with noexec,nosuid,nodev for hardening; prevents it from impacting other partitions.
  • 32 GiB swap: With up to 96 GB RAM and potential GPU/CUDA workloads, 32 GiB provides comfortable headroom for memory pressure without being wasteful. If you run memory-intensive AI/ML inference, consider increasing this.
  • Large /srv: The bulk of a headless server's storage goes here — VM disk images, container volumes, datasets, NFS exports, and similar.
  • Separate /boot: Ensures the bootloader and kernel images are always accessible regardless of root filesystem issues.

4. Post-Install: First Boot Setup

Log in as root at the terminal (using password set from prior steps).

  • Verify network connectivity:
ip addr show
ping -c 3 devuan.org
  • Update the system:
apt update && apt upgrade -y
  • Configure APT sources:
nano /etc/apt/sources.list
# Ensure contrib, non-free, and non-free-firmware are enabled

deb http://deb.devuan.org/merged excalibur main contrib non-free non-free-firmware
deb http://deb.devuan.org/merged excalibur-updates main contrib non-free non-free-firmware
deb http://deb.devuan.org/merged excalibur-security main contrib non-free non-free-firmware
  • Update again:
apt update
  • Install essential server packages:
apt install -y \
  sudo vim neovim htop tmux curl wget git \
  lm-sensors smartmontools nvme-cli \
  unattended-upgrades apt-listchanges \
  ufw \
  build-essential dkms linux-headers-amd64
  • Add user to sudo:
usermod -aG sudo yourusername
  • Create a Swap File (if you created the Simple Partition Layout)

Swap provides a safety buffer when system memory is exhausted. While modern systems with high RAM can run without swap, it is still recommended for stability especially when running Docker containers, databases, or GPU workloads.

Instead of creating a dedicated swap partition with the Simple Partition layout, using a swap file is preferred. It is easier to manage, resize, and remove without repartitioning disks.

The example below creates a 16 GB swap file. Adjust the size based on your system:

fallocate -l 16G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile

Add the following line to /etc/fstab so the swap file is enabled on boot:

echo '/swapfile none swap sw 0 0' >> /etc/fstab

Verify Swap

swapon --show
free -h

Sizing Guidance

  • 16 GB RAM or less: 8–16 GB swap recommended
  • 32–64 GB RAM: 8–16 GB swap sufficient
  • 64 GB+ RAM: 4–16 GB swap for safety buffer
  • Heavy GPU / AI workloads: Consider 16–32 GB swap

5. Install and Configure Tailscale

Tailscale provides secure, zero-config WireGuard-based mesh networking. All SSH access will go through the Tailscale network, meaning no SSH port is exposed to the public internet.

  • Devuan Excalibur is based on Debian Trixie, so use the Trixie repository:
mkdir -p --mode=0755 /usr/share/keyrings

curl -fsSL https://pkgs.tailscale.com/stable/debian/trixie.noarmor.gpg \
  | tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null

curl -fsSL https://pkgs.tailscale.com/stable/debian/trixie.tailscale-keyring.list \
  | tee /etc/apt/sources.list.d/tailscale.list

apt update
  • Install Tailscale:
apt install -y tailscale

Important: Tailscale's official packages ship with a systemd service file but no sysvinit script. Since Devuan uses sysvinit, you need to create one manually after installing.

  • The package only includes a systemd unit, so you must create an init script manually:
cat > /etc/init.d/tailscaled << 'INITEOF'
#!/bin/sh
### BEGIN INIT INFO
# Provides:          tailscale
# Required-Start:    $local_fs $network $all
# Required-Stop:     $local_fs $network
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Tailscale daemon
# Description:       Runs the tailscaled mesh VPN daemon.
### END INIT INFO

. /lib/lsb/init-functions

PIDFILE=/var/run/tailscale.pid
LOGFILE=/var/log/tailscale.log
TAILSCALED=/usr/sbin/tailscaled

fail_unless_root() {
    if [ "$(id -u)" != '0' ]; then
        log_failure_msg "must be run as root"
        exit 1
    fi
}

case "$1" in
    start)
        fail_unless_root
        log_daemon_msg "Starting Tailscale daemon" "tailscaled"
        $TAILSCALED --cleanup
        start-stop-daemon --start --background --no-close \
            --exec $TAILSCALED \
            --pidfile "$PIDFILE" \
            --make-pidfile \
            -- \
            --state=/var/lib/tailscale/tailscaled.state \
            --socket=/run/tailscale/tailscaled.sock >> $LOGFILE 2>&1
        status=$?
        log_end_msg $status
        ;;

    stop)
        fail_unless_root
        log_daemon_msg "Stopping Tailscale daemon" "tailscaled"
        start-stop-daemon --stop --pidfile "$PIDFILE" \
            --remove-pidfile --retry 10
        status=$?
        log_end_msg $status
        ;;

    restart)
        $0 stop
        sleep 1
        $0 start
        ;;

    status)
        status_of_proc -p "$PIDFILE" "$TAILSCALED" "tailscaled"
        ;;

    *)
        echo "Usage: $0 {start|stop|restart|status}"
        exit 1
        ;;
esac
INITEOF

chmod +x /etc/init.d/tailscaled
  • Enable and start Tailscale:
update-rc.d tailscaled defaults
mkdir -p /run/tailscale
service tailscaled start
service tailscaled status
  • Authenticate to your Tailnet:
tailscale up

This will print a URL. Open it in a browser on another device, sign in to your Tailscale account, and authorize the machine. Once authenticated, verify connectivity:

tailscale status
tailscale ip -4

Note the Tailscale IP (typically 100.x.y.z). This is the address you will use for SSH.

  • Enable Tailscale SSH:

Tailscale SSH lets you authenticate SSH sessions through your Tailscale identity provider, eliminating the need for SSH keys or passwords entirely:

tailscale set --ssh

When using Tailscale SSH, connections are authenticated by your Tailscale ACLs instead of local SSH keys. You can configure who has access in the Tailscale admin console under Access Controls.

Note: If you enabled Tailscale SSH in step 5.6.6, you can optionally disable the local SSH daemon entirely since Tailscale handles SSH directly:

service ssh stop
update-rc.d ssh disable
  • Test SSH over Tailscale from your other machine before disconnecting the monitor:

You can also use the name of your machine on your Tailnet instead of the Tailscale IP, if you have Tailscale MagicDNS enabled.

  • Configure the Firewall:

Since SSH is only accessible via Tailscale, the firewall should block SSH from the public interfaces entirely. Only open ports for services you explicitly need on the LAN.

ufw default deny incoming
ufw default allow outgoing

# Allow all traffic on the Tailscale interface (trusted mesh network)
ufw allow in on tailscale0

# Do NOT allow SSH on public interfaces — it is Tailscale-only
# ufw allow ssh  <-- intentionally omitted

ufw enable

If you later run services that need LAN access (e.g., NFS, a web server, Samba), add rules for those specific ports on the specific LAN interfaces:

# Example: allow HTTP on the 2.5G LAN interface only
ufw allow in on enp2s0 to any port 80 proto tcp

6. Install NVIDIA GPU Drivers

The open-source Nouveau driver must be disabled before installing the proprietary NVIDIA driver:

cat > /etc/modprobe.d/blacklist-nouveau.conf << 'EOF'
blacklist nouveau
options nouveau modeset=0
EOF

update-initramfs -u
reboot

After rebooting, log back in and install:

sudo apt update
sudo apt install linux-headers-$(uname -r) build-essential libglvnd-dev pkg-config dkms

Detect and install driver:

sudo nvidia-detect
sudo apt install nvidia-driver nvidia-kernel-dkms nvidia-smi nvidia-settings

Only use backports if the default driver fails for your GPU:

sudo apt install nvidia-driver firmware-misc-nonfree

Verify DKMS build:

dkms status

You should see a line like:

nvidia/550.163.01, 6.12.x-amd64, x86_64: installed

Required reboot and verify:

reboot

After reboot, confirm the driver is loaded:

nvidia-smi

Expected output will show the GPU with driver version, CUDA version, temperature, and memory usage. For a headless server with no display attached, nvidia-smi is the primary way to verify the GPU is operational.

  • Enable Persistence Mode (Headless):

Without an X server running, the NVIDIA driver may unload between GPU tasks, adding latency. Enable persistence mode:

nvidia-smi -pm 1

To make this persistent across reboots, create an init script. For sysvinit:

cat > /etc/init.d/nvidia-persistenced << 'INITEOF'
#!/bin/sh
### BEGIN INIT INFO
# Provides:          nvidia-persistenced
# Required-Start:    $local_fs
# Required-Stop:     $local_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: NVIDIA Persistence Daemon
### END INIT INFO

case "$1" in
  start)
    /usr/bin/nvidia-smi -pm 1
    ;;
  stop)
    /usr/bin/nvidia-smi -pm 0
    ;;
  *)
    echo "Usage: $0 {start|stop}"
    exit 1
    ;;
esac
exit 0
INITEOF


chmod +x /etc/init.d/nvidia-persistenced
update-rc.d nvidia-persistenced defaults
  • Install CUDA Toolkit:

If you need CUDA for compute workloads (AI inference, GPU-accelerated applications):

apt install -y nvidia-cuda-toolkit

Verify:

nvcc --version

7. Install Docker

Docker CE's official Debian packages include a sysvinit init script (/etc/init.d/docker), so Docker runs natively on Devuan without systemd.

  • Remove Conflicting Packages
for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do
    apt-get remove -y $pkg 2>/dev/null
done
  • Add the Docker Repository

Since Devuan Excalibur is based on Debian Trixie, use the Trixie repository:

apt install -y ca-certificates curl gnupg

install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc

cat > /etc/apt/sources.list.d/docker.list << EOF
deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian trixie stable
EOF


apt update
  • Install Docker Engine
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
  • Enable and Start Docker (sysvinit)

The Docker package ships with /etc/init.d/docker. Enable it at boot and start it:

update-rc.d docker defaults
service docker start

Verify Docker is running:

service docker status
docker info
  • Allow Your User to Run Docker

Add your regular user to the docker group so you don't need sudo for every Docker command:

usermod -aG docker yourusername

Log out and back in (or newgrp docker) for the group change to take effect.

Security note: Membership in the docker group grants root-equivalent access to the host. Only add trusted users.

  • Test the Installation
docker run --rm hello-world
  • NVIDIA Container Toolkit (GPU Containers)

To use the NVIDIA GPU inside Docker containers (for CUDA, AI inference, etc.), install the NVIDIA Container Toolkit:

curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
  | gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg

curl -fsSL https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \
  | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
  | tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

apt update
apt install -y nvidia-container-toolkit

Configure the Docker runtime:

nvidia-ctk runtime configure --runtime=docker
service docker restart

Test GPU access from a container:

docker run --rm --gpus all nvidia/cuda:12.4.0-base-ubuntu22.04 nvidia-smi

You should see the NVIDIA GPU listed with driver version and CUDA version.

Docker Compose Quick Reference

Docker Compose is installed as a CLI plugin. Use it with:

docker compose up -d          # Start services in background

docker compose down            # Stop and remove services

docker compose logs -f         # Follow logs

docker compose ps              # List running services

8. NFS NAS Mounts

If you are mounting external storage NAS drives (e.g., Synology, QNAP, TrueNAS), follow instructions below.

  • Install NFS Client Packages
apt install -y nfs-common
  • Create Mount Points

Create directories for each NFS share you want to mount. A clean convention is to mount them under /mnt/nas/:

mkdir -p /mnt/nas/media
mkdir -p /mnt/nas/backups
mkdir -p /mnt/nas/shared

Adjust the names to match your NAS export structure.

  • Test Mounts Manually

Before making them permanent, verify each mount works:

mount -t nfs4 nas.local:/volume1/media /mnt/nas/media
ls /mnt/nas/media

Replace nas.local with the IP or hostname of your NAS, and /volume1/media with the actual NFS export path. If using NFSv3:

mount -t nfs -o vers=3 nas.local:/volume1/media /mnt/nas/media
  • Configure Permanent Mounts in /etc/fstab

Once the manual test succeeds, add entries to /etc/fstab for automatic mounting at boot:

nano /etc/fstab

# NAS NFS Mounts
nas.local:/volume1/media    /mnt/nas/media    nfs4  rw,soft,intr,timeo=30,retrans=3,_netdev  0  0
nas.local:/volume1/backups  /mnt/nas/backups  nfs4  rw,soft,intr,timeo=30,retrans=3,_netdev  0  0
nas.local:/volume1/shared   /mnt/nas/shared   nfs4  rw,soft,intr,timeo=30,retrans=3,_netdev  0  0

Mount options explained:

  • rw: Read-write access. Use ro for read-only shares like media libraries.
  • soft: Returns an error if the NAS is unreachable instead of hanging indefinitely. Critical for a headless server — a hard mount will cause processes to freeze and become unkillable if the NAS goes offline.
  • intr: Allows NFS operations to be interrupted by signals (e.g., Ctrl+C).
  • timeo=30: Timeout in deciseconds (3 seconds) before retrying.
  • retrans=3: Number of retries before reporting failure.
  • _netdev: Tells the init system this mount requires the network to be up first, preventing boot hangs if the NAS is unreachable.

Mount all new fstab entries:

mount -a

Verify:

df -h | grep nas
  • NFS Mount Permissions

NFS permissions depend on how your NAS exports are configured. Common approaches:

UID/GID mapping (recommended): Ensure the UID and GID of your Devuan user match the NFS export's expected UID/GID. Check with id yourusername on Devuan and compare against the NAS settings.

all_squash with anonuid/anongid: If the NAS export uses all_squash, all access is mapped to a single UID/GID defined on the NAS side. This is the simplest for shared access.

No root squash: Only enable no_root_squash on the NAS if you specifically need root write access from this server. This is a security risk and is generally unnecessary.

  • Using NFS Mounts in Docker Containers

Bind-mount the host NFS path

In your compose.yaml:

yaml

services:
  myapp:
    image: myapp:latest
    volumes:
      - /mnt/nas/media:/data/media
      - /mnt/nas/shared:/data/shared

This is the simplest approach. The host handles the NFS connection and containers see the data as a regular directory.

  • Monitoring NFS Mount Health

NFS mounts can silently become stale if the NAS reboots or the network hiccups. Create a simple health check cron job:

cat > /etc/cron.d/nfs-health << 'EOF'
*/5 * * * * root /usr/local/bin/nfs-health-check.sh
EOF


cat > /usr/local/bin/nfs-health-check.sh << 'SCRIPT'
#!/bin/sh
for mount in /mnt/nas/media /mnt/nas/backups /mnt/nas/shared; do
    if mountpoint -q "$mount"; then
        timeout 5 ls "$mount" > /dev/null 2>&1
        if [ $? -ne 0 ]; then
            logger -t nfs-health "STALE mount detected: $mount — attempting remount"
            umount -l "$mount" 2>/dev/null
            mount "$mount"
        fi
    else
        logger -t nfs-health "Mount missing: $mount — attempting mount"
        mount "$mount"
    fi
done
SCRIPT


chmod +x /usr/local/bin/nfs-health-check.sh

This checks every 5 minutes that each NFS mount is responsive, and attempts a remount if a share has gone stale. Check the results in /var/log/syslog with grep nfs-health /var/log/syslog.

9. Fastfetch

"Obligatory neofetch" (fastfetch):

sudo apt update
sudo apt install fastfetch

Result

  • No systemd
  • Full Docker support
  • GPU acceleration working
  • NAS mounts