JustToThePoint English Website Version
JustToThePoint en español

Arch Linux Post-Installation: NFS, Fstab, and MIME Configuration

All those who seem stupid, they are, and also so are half of those who do not, Quevedo

image info

Introduction

Continuing our Arch Linux post-installation journey, we now focus on system integration and refining the user experience. Having established a Samba share for universal compatibility, we now turn our attention to NFS (Network File System). This script configures a dedicated NFS server for high-performance, low-overhead file sharing specifically between Linux machines on the local network.

Beyond networking, we ensure our data storage is persistent and accessible by configuring /etc/fstab to automatically mount drives at boot. Finally, we polish the desktop environment by setting up MIME types and file associations. We configure xdg-open to correctly handle different file types—launching Neovim for code, MPV for media, and Zathura for PDFs—and create a custom desktop entry to seamlessly open terminal applications from the graphical file manager.

NFS

#!/usr/bin/env bash
# ── 15-nfs.sh ─────────────────────────────────────────────────────────────────
# Install and configure an NFS server.
#
# NFS vs Samba:
#   NFS   — Unix-native protocol. Fast, low overhead, perfect for Linux↔Linux.
#   Samba — SMB/CIFS protocol. Works with Windows, macOS, and Linux.
#           More overhead, but universal compatibility.
#
# Run: bash 15-nfs.sh
# ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail

# ── Configuration ─────────────────────────────────────────────────────────────
NFS_SHARE="/mnt/data"          # Directory to export
LAN_SUBNET="192.168.1.0/24"    # We will only allow clients from our local network
SERVER_IP="192.168.1.46"       # Our NFS server IP (update if different)

echo "── 15-nfs: installing and configuring NFS server..."

# ── Install ───────────────────────────────────────────────────────────────────
sudo pacman -S --needed --noconfirm nfs-utils

# ── Create the export directory ───────────────────────────────────────────────
sudo mkdir -p "$NFS_SHARE"

# VS Code Remote SSH (NixOS) connect to Arch as your_main_user (e.g., nmaximo7), typically UID 1000/1000
# Be careful, there may be a UID/GID mismatch
sudo chown -R 1000:1000 /mnt/data/

# 755: owner read/write/execute; others read/execute.
sudo chmod 755 "$NFS_SHARE"

echo "  ✅  $NFS_SHARE created"

# ── /etc/exports ──────────────────────────────────────────────────────────────
# Each line:  ()
#
# Export options explained:
#   rw               — clients can read and write
#   sync             — wait for writes to be committed to disk before replying.
#                      Slower than async but safer (no data loss on crash).
#   no_subtree_check — disable subtree checking. Recommended modern default.
#                      Subtree checking causes problems when files are renamed.
#   no_root_squash   — let root on the client act as root on the server.
#                      Only use if you fully trust all LAN clients.
echo "── Writing /etc/exports..."
sudo tee /etc/exports > /dev/null << EXPORTS
# /etc/exports — NFS export table
# After editing: sudo exportfs -ra && sudo systemctl restart nfs-server
# Check active exports: sudo exportfs -v

# Share /mnt/data with all clients on 192.168.1.0/24
${NFS_SHARE}  ${LAN_SUBNET}(rw,sync,no_subtree_check,no_root_squash)

# Read-only example (uncomment to add a read-only media share):
# /mnt/media  ${LAN_SUBNET}(ro,sync,no_subtree_check,no_root_squash)
EXPORTS

echo "  ✅  /etc/exports written"

# ── Apply exports ─────────────────────────────────────────────────────────────
# exportfs -ra: re-read /etc/exports and sync with the kernel NFS table
sudo exportfs -ra
echo "  ✅  Exports applied"

# ── Enable and start NFS server ───────────────────────────────────────────────
# nfs-server.service is the main NFS daemon.
# rpcbind maps RPC program numbers to ports (required for NFSv3).
# NFSv4 only strictly needs nfs-server, but rpcbind is harmless to have.
sudo systemctl enable --now nfs-server rpcbind 2>/dev/null || \
  sudo systemctl enable --now nfs-server

echo "  ✅  nfs-server started and enabled"

# ── Firewall ──────────────────────────────────────────────────────────────────
if command -v ufw &>/dev/null; then
  sudo ufw allow from "$LAN_SUBNET" to any port 2049
  sudo ufw allow from "$LAN_SUBNET" to any port 111
  echo "  ✅  ufw: NFS ports opened for $LAN_SUBNET"
fi

# ── Verify ────────────────────────────────────────────────────────────────────
echo
echo "── Active exports:"
sudo exportfs -v

echo
echo "── NFS server status:"
sudo systemctl status nfs-server --no-pager | head -6

echo
echo "── Mount from another Linux machine:"
echo "   sudo mount ${SERVER_IP}:${NFS_SHARE} /mnt/remote"

echo
echo "── Add to /etc/fstab for auto-mount at boot:"
echo "   ${SERVER_IP}:${NFS_SHARE}  /mnt/remote  nfs  defaults,_netdev  0  0"
echo
echo "✅  15-nfs: done"

NFS Client

  fileSystems."/mnt/data" = {
     device = "192.168.1.46:/mnt/data";
     fsType = "nfs";
     options = [
       "x-systemd.automount"   # mount on first access, not at boot
       "noauto"                # do not mount during boot sequence
       "nofail"                # do not block boot even if the server is unreachable
       "soft"                  # return error instead of hanging forever if server goes down
       "timeo=30"              # 3-second timeout before returning error (units: 0.1s)
       "rw"                    # read-write (must also be rw in /etc/exports on the server)
       "nfsvers=4"
       "nolock"                # important for VS Code — avoids lock-related hangs
     ];
   };
  # ── User — must have uid = 1000 to match Arch file ownership ─────────────────
  users.groups.nmaximo7 = {
  	gid = 1000;
  };

  # Define a user account. Don't forget to set a password with ‘passwd’.
  users.users.nmaximo7 = {
    isNormalUser = true;
    uid = 1000; # This must match Arch's nmaximo7 UID
    group = "nmaximo7";
    description = "nmaximo7";
    extraGroups = [ "networkmanager" "wheel" "docker" "audio"];
    shell = "${pkgs.zsh}/bin/zsh";
    packages = with pkgs; [
    #  thunderbird
    ];
  };

This script does two unrelated things:

  1. Adds a data drive to /etc/fstab so that /mnt/data is automatically mounted at boot.
  2. Sets MIME type defaults so xdg-open launches the right application for each file type (images with imv, PDFs with zathura, text files with Neovim inside Alacritty, etc.).
#!/usr/bin/env bash
# ── 16-mime.sh ────────────────────────────────────────────────────────────────
# Set sensible MIME type defaults so xdg-open opens files with the right app.
# Also adds the /mnt/data drive to /etc/fstab for auto-mount at boot.
#
# Run: bash 16-mime.sh
# ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail

# Determine the real user (even if script is run with sudo)
USERNAME="${SUDO_USER:-$(whoami)}"

# ══════════════════════════════════════════════════════════════════════════════
# A. /etc/fstab — mount /mnt/data at boot
# ══════════════════════════════════════════════════════════════════════════════
#
# The drive UUID is stable across reboots and device name changes.
# ext4 defaults means rw, suid, dev, exec, auto, nouser, async.
# The two trailing zeros: first = dump (0 = skip or do not dump),
# second = 2 (check at boot after the root partition, usually 1 for /).
#
DATA_UUID="ca04070e-a366-424b-8fa3-c9960c9fbdae"
DATA_MOUNT="/mnt/data"
FSTAB_LINE="UUID=${DATA_UUID}  ${DATA_MOUNT}  ext4  defaults  0  2"

echo "── A. Adding /mnt/data to /etc/fstab..."
sudo mkdir -p "$DATA_MOUNT"

if grep -q "$DATA_UUID" /etc/fstab; then
  echo "  ✅  /mnt/data already in /etc/fstab — skipping"
else
  echo "" | sudo tee -a /etc/fstab > /dev/null
  echo "# /mnt/data — data drive (added by 16-mime.sh)" | sudo tee -a /etc/fstab > /dev/null
  echo "$FSTAB_LINE" | sudo tee -a /etc/fstab > /dev/null
  echo "  ✅  Added to /etc/fstab: $FSTAB_LINE"
fi

# Mount immediately without rebooting
sudo mount -a 2>/dev/null && echo "  ✅  Drive mounted at $DATA_MOUNT"   || echo "  ⚠️  mount -a failed — drive may not be connected yet (will mount at boot)"

# ══════════════════════════════════════════════════════════════════════════════
# B. MIME TYPE DEFAULTS
# ══════════════════════════════════════════════════════════════════════════════
#
# xdg-open reads or consults ~/.config/mimeapps.list to decide which desktop application to launch
# for a given file type. Without this file, xdg-open either does nothing
# or launches the wrong app.
#
# Format: mime/type=app.desktop
# Find .desktop files in: /usr/share/applications/ and ~/.local/share/applications/
#
# How to find a mime type:
#   file --mime-type myfile.pdf     → application/pdf
#   xdg-mime query filetype myfile  → same thing
#
# How to find a .desktop file name:
#   ls /usr/share/applications/ | grep -i firefox
#
echo "── B. Setting MIME type defaults..."

# Ensure the user's config directories exist
mkdir -p "$HOME/.config"

cat > "$HOME/.config/mimeapps.list" << 'MIMEAPPS'
# ── ~/.config/mimeapps.list ───────────────────────────────────────────────────
# Default application associations for xdg-open.
# Format: mime/type=application.desktop
# Multiple apps: mime/type=first.desktop;second.desktop;
#   xdg-open uses the first one that exists on the system.
# Edit this file or use: xdg-mime default app.desktop mime/type
# ─────────────────────────────────────────────────────────────────────────────

[Default Applications]

# ── Directories ───────────────────────────────────────────────────────────────
# Directories → thunar.desktop
inode/directory=thunar.desktop

# ── Web / URLs ────────────────────────────────────────────────────────────────
# Web links → firefox.desktop
x-scheme-handler/http=firefox.desktop
x-scheme-handler/https=firefox.desktop
x-scheme-handler/ftp=firefox.desktop
x-scheme-handler/chrome=firefox.desktop
text/html=firefox.desktop
application/xhtml+xml=firefox.desktop

# ── Text and code (Neovim via Alacritty) ─────────────────────────────────────
# Text files → nvim-open.desktop (custom)
# nvim-open.desktop is created below — wraps `alacritty -e nvim %f`
text/plain=nvim-open.desktop
text/markdown=nvim-open.desktop
text/x-shellscript=nvim-open.desktop
text/x-python=nvim-open.desktop
text/x-lua=nvim-open.desktop
text/x-csrc=nvim-open.desktop
text/x-chdr=nvim-open.desktop
text/css=nvim-open.desktop
text/javascript=nvim-open.desktop
application/json=nvim-open.desktop
application/x-shellscript=nvim-open.desktop
application/xml=nvim-open.desktop
application/toml=nvim-open.desktop

# ── Documents ─────────────────────────────────────────────────────────────────
# PDFs → zathura.desktop
application/pdf=org.pwmt.zathura.desktop
application/vnd.oasis.opendocument.text=soffice.desktop
application/msword=soffice.desktop
application/vnd.openxmlformats-officedocument.wordprocessingml.document=soffice.desktop
application/vnd.ms-excel=soffice.desktop
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet=soffice.desktop

# ── Images ────────────────────────────────────────────────────────────────────
# Images → imv.desktop
image/png=imv.desktop
image/jpeg=imv.desktop
image/gif=imv.desktop
image/webp=imv.desktop
image/svg+xml=imv.desktop
image/bmp=imv.desktop
image/tiff=imv.desktop

# ── Video ─────────────────────────────────────────────────────────────────────
# Video/audio → mpv.desktop
video/mp4=mpv.desktop
video/mkv=mpv.desktop
video/x-matroska=mpv.desktop
video/webm=mpv.desktop
video/avi=mpv.desktop
video/quicktime=mpv.desktop
video/x-msvideo=mpv.desktop

# ── Audio ─────────────────────────────────────────────────────────────────────
audio/mpeg=mpv.desktop
audio/flac=mpv.desktop
audio/ogg=mpv.desktop
audio/wav=mpv.desktop
audio/x-wav=mpv.desktop
audio/mp4=mpv.desktop
audio/aac=mpv.desktop

# ── Archives ──────────────────────────────────────────────────────────────────
# Archives → thunar.desktop
application/zip=thunar.desktop
application/x-tar=thunar.desktop
application/x-7z-compressed=thunar.desktop
application/gzip=thunar.desktop
application/x-bzip2=thunar.desktop
application/x-rar=thunar.desktop

# ── Email ─────────────────────────────────────────────────────────────────────
x-scheme-handler/mailto=firefox.desktop

# ── Torrent ───────────────────────────────────────────────────────────────────
# x-scheme-handler/magnet=transmission-gtk.desktop
# application/x-bittorrent=transmission-gtk.desktop

[Added Associations]
text/plain=nvim-open.desktop;
inode/directory=thunar.desktop;
image/png=imv.desktop;
image/jpeg=imv.desktop;
video/mp4=mpv.desktop;
audio/mpeg=mpv.desktop;
application/pdf=org.pwmt.zathura.desktop;
MIMEAPPS

echo "  ✅  ~/.config/mimeapps.list written"

# ── Create nvim-open.desktop ──────────────────────────────────────────────────
# xdg-open can't launch terminal apps directly — it needs a .desktop file
# that specifies Terminal=true or wraps the command in a terminal emulator.
# We create a custom .desktop that opens files in Neovim inside Alacritty.
mkdir -p "$HOME/.local/share/applications"
cat > "$HOME/.local/share/applications/nvim-open.desktop" << 'NVIMDESKTOP'
[Desktop Entry]
Name=Neovim (Alacritty)
Comment=Open file in Neovim inside Alacritty terminal
# %f = single file path passed by xdg-open
Exec=alacritty -e nvim %f
Type=Application
# Terminal=false because we launch our own terminal (Alacritty) explicitly
Terminal=false
Categories=TextEditor;Development;
MimeType=text/plain;text/markdown;text/x-shellscript;text/x-python;text/css;application/json;application/toml;
Icon=nvim
StartupNotify=false
NVIMDESKTOP

echo "  ✅  nvim-open.desktop created"

# ── Update the desktop database ───────────────────────────────────────────────
# We do so xdg-open picks up the new mimeapps.list and .desktop files
if command -v update-desktop-database &>/dev/null; then
  update-desktop-database "$HOME/.local/share/applications"
  echo "  ✅  Desktop database updated"
fi

# ── Install missing apps referenced in mimeapps.list ─────────────────────────
echo "── Installing apps referenced in mimeapps.list..."
sudo pacman -S --needed --noconfirm \
  alacritty \
  imv       \   # image viewer (Wayland-native)
  mpv       \   # media player
  thunar    \   # file manager
  zathura   \   # PDF viewer (minimal, keyboard-driven)
  zathura-pdf-mupdf     # PDF backend for zathura

# LibreOffice (for Office documents) — large download, comment out if needed
# sudo pacman -S --needed --noconfirm libreoffice-fresh

echo
echo "── Verify a MIME association:"
echo "   xdg-mime query default text/plain"
echo "   xdg-mime query default inode/directory"
echo "   xdg-mime query default image/png"

echo
echo "✅  16-mime: done"
echo
echo "══════════════════════════════════════════════════════════"
echo "  Full installation complete! Run: sudo reboot"
echo "══════════════════════════════════════════════════════════"
Bitcoin donation

JustToThePoint Copyright © 2011 - 2026 Anawim. ALL RIGHTS RESERVED. Bilingual e-books, articles, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun. Social Issues, Join us.

This website uses cookies to improve your navigation experience.
By continuing, you are consenting to our use of cookies, in accordance with our Cookies Policy and Website Terms and Conditions of use.