Sometimes people don’t want to hear the truth because they don’t want their illusions destroyed, Friedrich Nietzsche.

With the core system settings and remote access configured in the previous steps, we now turn our attention to multimedia capabilities and establishing a productive development environment. This section combines two essential post-installation scripts: 09-audio.sh and 10-development.sh.
First, we transition from the legacy PulseAudio server to PipeWire, the modern standard for audio and video routing on Linux. The script handles the removal of conflicting packages, installs the necessary compatibility layers (for PulseAudio, ALSA, and JACK), and configures Bluetooth support. Crucially, it sets up PipeWire as a user service, ensuring that audio permissions and processes are handled correctly within your specific user session rather than at the system level.
Next, we build a robust toolkit for developers. This includes installing the Hugo static site generator (extended edition), setting up Visual Studio Code via the AUR to ensure access to the proprietary marketplace, and configuring a Python environment with pipx for isolated tool management. Whether you are coding, writing, or managing servers, these tools form the backbone of a functional Arch Linux workstation.
#!/usr/bin/env bash
# ── 09-audio.sh ───────────────────────────────────────────────────────────────
# Installs and configures the PipeWire audio/video routing daemon as a
# complete replacement for PulseAudio and JACK. Enables low-latency
# professional audio, Bluetooth codecs, and seamless application compatibility.
#
# Architecture:
# pipewire — Core daemon handling audio/video streams and routing
# wireplumber — Session/policy manager (controls which app gets audio)
# pipewire-pulse — PulseAudio compatibility layer
# pipewire-alsa — ALSA compatibility layer
# pipewire-jack — JACK compatibility layer
#
# Why PipeWire replaces both PulseAudio AND JACK:
# Unified stack — PipeWire handles consumer and pro audio simultaneously with
# one daemon, lower latency, and better Wayland/Bluetooth support.
#
# Run: bash 09-audio.sh
# ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail # Strict mode
echo "── 09-audio: installing PipeWire stack..."
# ── Remove conflicting audio servers ──────────────────────────────────────────
# PulseAudio and PipeWire cannot run simultaneously.
# On a fresh Arch install this is unlikely, but worth checking.
for pkg in pulseaudio pulseaudio-bluetooth pulseaudio-alsa jack2; do
if pacman -Qi "$pkg" &>/dev/null; then
echo " ⚠️ Removing conflicting package: $pkg"
sudo pacman -R --noconfirm "$pkg"
fi
done
# ── Install PipeWire and dependencies ────────────────────────────────────────────────────
# IMPORTANT: inline comments (#) cannot appear after package names on the
# same line inside a pacman -S call — the shell passes them as package names.
# Always put comments on separate lines or use an array.
sudo pacman -S --needed --noconfirm \
pipewire \
pipewire-alsa \
pipewire-pulse \
pipewire-jack \
wireplumber \
pavucontrol \
playerctl \
pamixer
# ── Bluetooth audio ───────────────────────────────────────────────────────────
# Same rule applies: comments must be on separate lines.
sudo pacman -S --needed --noconfirm \
bluez \
bluez-utils \
blueman
sudo systemctl enable bluetooth
# ── Enable PipeWire as user services ─────────────────────────────────────────
# PipeWire runs as user services (not system service) — one instance per user.
# systemctl --user manages services that run in the user's login session.
#
# Why user services instead of system services:
# Audio is per-user. Running PipeWire as a system service would mean all
# users share one audio daemon, which causes permission conflicts.
# The user service is started automatically at login.
systemctl --user enable pipewire
systemctl --user enable pipewire-pulse
systemctl --user enable wireplumber
# Start immediately if we're already in a graphical user session.
# The 2>/dev/null suppresses "Failed to connect to bus" errors that appear
# when this script runs from a TTY before a user session is active.
systemctl --user start pipewire 2>/dev/null || echo " ℹ️ pipewire: will start after next login"
systemctl --user start pipewire-pulse 2>/dev/null || echo " ℹ️ pipewire-pulse: will start after next login"
systemctl --user start wireplumber 2>/dev/null || echo " ℹ️ wireplumber: will start after next login"
# ── Verify PipeWires installation───────────────────────────────────────────────────────
echo
echo "── PipeWire status:"
if pactl info 2>/dev/null | grep -q "PipeWire"; then
# If pactl is talking to a PipeWire-backed PulseAudio server, pactl info will contain something like:
# Server Name: PulseAudio (on PipeWire 1.0.5)
echo " ✅ PipeWire is running as PulseAudio replacement"
pactl info | grep "Server Name"
else
echo " ⚠️ PipeWire not yet running — will start after next login"
fi
echo "── Audio sinks (output devices):"
pactl list short sinks 2>/dev/null || echo " (available after next login)"
# This prints the available output devices, or a fallback message if pactl can’t talk to the server.
echo
echo "✅ 09-audio: done"
echo " Next: bash 10-development.sh"
#!/usr/bin/env bash
# ── 10-development.sh ─────────────────────────────────────────────────────────
# Install development tools: Hugo (static site generator), VS Code with essential extensions,
# Python, and VS Code extensions.
# Run: bash 10-development.sh
# ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail
echo "── 10-development: installing development tools..."
# ── Hugo (static site generator) ─────────────────────────────────────────────
# Hugo is available in the official Arch repos.
echo "── Installing Hugo..."
sudo pacman -S --needed --noconfirm hugo
echo " ✅ Hugo: $(hugo version | head -1)"
# ── VS Code (via AUR) ─────────────────────────────────────────────────────────
# visual-studio-code-bin is the official Microsoft binary from the AUR.
# It includes the proprietary marketplace (GitHub Copilot, Remote-SSH, etc.).
#
echo "── Installing VS Code (visual-studio-code-bin)..."
paru -S --needed --noconfirm visual-studio-code-bin
echo " ✅ VS Code: $(code --version | head -1)"
# ── VS Code extensions ────────────────────────────────────────────────────────
# Installed via `code --install-extension `.
# Extensions are installed to ~/.vscode/extensions/ by default.
# Reference: https://marketplace.visualstudio.com/vscode
#
echo "── Installing VS Code extensions..."
EXTENSIONS=(
# ── Python ────────────────────────────────────────────────────────────────
ms-python.python # Python language support + debugger
ms-python.pylance # Fast Python language server
ms-python.black-formatter # Black auto-formatter
charliermarsh.ruff # Ruff linter (fast Python linter in Rust)
# ── Remote development ────────────────────────────────────────────────────
ms-vscode-remote.remote-ssh # SSH into your server
ms-vscode-remote.remote-containers # Dev containers support
# ── Writing / docs ────────────────────────────────────────────────────────
yzhang.markdown-all-in-one # Markdown preview, shortcuts, TOC
streetsidesoftware.code-spell-checker # Spell checker for code and docs
esbenp.prettier-vscode # Prettier formatter (JS/HTML/CSS/YAML)
# ── Appearance ────────────────────────────────────────────────────────────
dracula-theme.theme-dracula # Dracula colour theme
pkief.material-icon-theme # File icons
# ── Web / Hugo ────────────────────────────────────────────────────────────
budparr.language-hugo-vscode # Hugo template syntax highlighting
tamasfe.even-better-toml # TOML support (Hugo config files)
# ── Nix ───────────────────────────────────────────────────────────────────
jnoortheen.nix-ide # Nix language support + formatting
# ── Git ───────────────────────────────────────────────────────────────────
eamodio.gitlens # Git blame, history, and visualisation
)
INSTALLED=0
FAILED=0
for ext in "${EXTENSIONS[@]}"; do
# Skip comment lines (lines starting with #)
[[ "$ext" =~ ^# ]] && continue
[[ -z "$ext" ]] && continue
if code --install-extension "$ext" --force 2>/dev/null; then
echo " ✅ $ext"
(( INSTALLED++ )) || true
else
echo " ⚠️ Failed: $ext"
(( FAILED++ )) || true
fi
done
echo " Extensions: $INSTALLED installed, $FAILED failed"
# ── Python ────────────────────────────────────────────────────────────────────
echo "── Installing Python tools..."
sudo pacman -S --needed --noconfirm \
python \
python-pip \
python-pipx \
python-virtualenv
echo "Sometimes, you need to install pip inside the venv"
echo "Activate the venv, then run: python -m ensurepip --upgrade"
# Install commonly used Python CLI tools via pipx (isolated, no venv needed)
# pipx installs each tool in its own venv but exposes the binary globally
pipx install poetry 2>/dev/null || true # dependency manager
pipx install httpie 2>/dev/null || true # HTTPie (http CLI client)
pipx install buku 2>/dev/null || true # A bookmark manager
echo " ✅ Python $(python3 --version)"
# ── Node.js (for some VS Code extensions and web tools) ──────────────────────
echo "── Installing Node.js..."
sudo pacman -S --needed --noconfirm \
nodejs \
npm
echo " ✅ Node.js $(node --version)"
echo
echo "✅ 10-development: done"
echo " Next: bash 11-dotfiles.sh"