Building Your Environment#

This capstone lesson ties everything together. You will build a complete power-user Linux environment from scratch — dotfiles, tools, shell configuration, SSH keys, tmux, firewall, backups, and scheduled tasks. Each section references the module where the concept was taught.

This is a guided project, not a copy-paste exercise. Adapt every step to your preferences.


Part 1: Essential Tools#

References: Module 04 — Package Management

Install the tools you will use throughout:

sudo apt update && sudo apt upgrade -y

sudo apt install -y \
    git curl wget \
    vim tmux htop \
    tree ncdu \
    ripgrep fd-find \
    net-tools \
    unzip zip \
    build-essential \
    fail2ban ufw \
    plocate

ripgrep (rg) is a faster alternative to grep. fd-find (fdfind on Debian/Ubuntu) is a faster alternative to find. Both are optional but excellent productivity tools.


Part 2: Shell Configuration#

References: Module 12 — Shell Customization

.bashrc#

Build a .bashrc that makes your terminal comfortable:

cat >> ~/.bashrc << 'BASHRC'

# --- Custom Configuration ---

# History
HISTSIZE=50000
HISTFILESIZE=100000
HISTCONTROL=ignoreboth:erasedups
shopt -s histappend

# Shell options
shopt -s checkwinsize
shopt -s cdspell
shopt -s autocd

# Prompt: green user@host, blue directory, git branch
parse_git_branch() {
    git branch 2>/dev/null | grep '^\*' | sed 's/* //'
}
PS1='\[\e[1;32m\]\u@\h\[\e[0m\]:\[\e[1;34m\]\w\[\e[0m\]'
PS1+='$( branch=$(parse_git_branch); [ -n "$branch" ] && echo " (\[\e[33m\]$branch\[\e[0m\])" )'
PS1+='\$ '

# Aliases
alias ll='ls -alF --color=auto'
alias la='ls -A --color=auto'
alias l='ls -CF --color=auto'
alias ..='cd ..'
alias ...='cd ../..'
alias grep='grep --color=auto'
alias df='df -h'
alias du='du -h'
alias free='free -h'
alias ports='ss -tlnp'
alias update='sudo apt update && sudo apt upgrade -y'
alias reload='source ~/.bashrc'

# Functions
mkcd() { mkdir -p "$1" && cd "$1"; }

extract() {
    case "$1" in
        *.tar.gz|*.tgz)  tar xzf "$1" ;;
        *.tar.bz2)       tar xjf "$1" ;;
        *.tar.xz)        tar xJf "$1" ;;
        *.zip)           unzip "$1" ;;
        *.gz)            gunzip "$1" ;;
        *.bz2)           bunzip2 "$1" ;;
        *)               echo "Unknown format: $1" ;;
    esac
}

# PATH
export PATH="$HOME/.local/bin:$HOME/bin:$PATH"

# Editor
export EDITOR="vim"
export VISUAL="vim"

BASHRC

source ~/.bashrc

.profile#

Ensure environment variables are set for login shells:

# Check that .profile sources .bashrc (it should on Ubuntu/Debian)
grep -q bashrc ~/.profile && echo "OK: .profile sources .bashrc"

Part 3: Vim Configuration#

References: Module 05 — vim

Create a minimal but useful .vimrc:

cat > ~/.vimrc << 'VIMRC'
" Basics
set nocompatible
syntax on
filetype plugin indent on

" Display
set number
set relativenumber
set cursorline
set showcmd
set showmatch
set wildmenu

" Indentation
set tabstop=4
set shiftwidth=4
set expandtab
set autoindent
set smartindent

" Search
set incsearch
set hlsearch
set ignorecase
set smartcase

" Behavior
set backspace=indent,eol,start
set scrolloff=5
set mouse=a
set clipboard=unnamedplus

" Status line
set laststatus=2
set statusline=%f\ %m%r%h%w\ %=%l/%L\ col:%c\ %p%%
VIMRC

Part 4: tmux Configuration#

References: Module 12 — tmux

cat > ~/.tmux.conf << 'TMUX'
# Remap prefix to Ctrl+a
unbind C-b
set -g prefix C-a
bind C-a send-prefix

# Split panes with | and -
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"

# New window in current path
bind c new-window -c "#{pane_current_path}"

# Navigate panes with Alt+arrow (no prefix needed)
bind -n M-Left select-pane -L
bind -n M-Right select-pane -R
bind -n M-Up select-pane -U
bind -n M-Down select-pane -D

# Resize panes
bind -r H resize-pane -L 5
bind -r J resize-pane -D 5
bind -r K resize-pane -U 5
bind -r L resize-pane -R 5

# Mouse support
set -g mouse on

# Start numbering at 1
set -g base-index 1
setw -g pane-base-index 1

# Renumber windows when one is closed
set -g renumber-windows on

# History
set -g history-limit 50000

# Reload config
bind r source-file ~/.tmux.conf \; display "Config reloaded!"

# Status bar
set -g status-style bg=colour235,fg=white
set -g status-left '[#S] '
set -g status-right '%H:%M %d-%b'
TMUX

Part 5: SSH Keys#

References: Module 11 — SSH

# Generate a key pair (if you don't have one)
ls ~/.ssh/id_ed25519 2>/dev/null || ssh-keygen -t ed25519 -C "$(whoami)@$(hostname)"

# View your public key (add this to GitHub, servers, etc.)
cat ~/.ssh/id_ed25519.pub

# Create an SSH config
cat > ~/.ssh/config << 'SSHCONFIG'
# Defaults
Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3
    AddKeysToAgent yes

# Example: add your servers here
# Host myserver
#     HostName 192.168.1.100
#     User kmiguel
#     IdentityFile ~/.ssh/id_ed25519
SSHCONFIG

chmod 600 ~/.ssh/config

Part 6: Firewall#

References: Module 14 — Firewall

# Set default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow SSH (adjust port if you changed it)
sudo ufw allow ssh

# Rate-limit SSH
sudo ufw limit ssh

# Enable
sudo ufw enable

# Verify
sudo ufw status verbose

Part 7: Backup Script#

References: Module 08 — Shell Scripting

Create a script that backs up your important files:

mkdir -p ~/bin

cat > ~/bin/backup.sh << 'BACKUP'
#!/bin/bash
set -euo pipefail

# Configuration
BACKUP_DIR="$HOME/backups"
DATE=$(date +%Y-%m-%d_%H%M)
ARCHIVE="$BACKUP_DIR/home-backup-$DATE.tar.gz"

# What to back up
SOURCES=(
    "$HOME/.bashrc"
    "$HOME/.profile"
    "$HOME/.vimrc"
    "$HOME/.tmux.conf"
    "$HOME/.ssh/config"
    "$HOME/bin"
    "$HOME/Documents"
)

# Create backup directory
mkdir -p "$BACKUP_DIR"

# Build the file list (only include existing files)
FILES=()
for src in "${SOURCES[@]}"; do
    if [ -e "$src" ]; then
        FILES+=("$src")
    fi
done

# Create the archive
tar czf "$ARCHIVE" "${FILES[@]}" 2>/dev/null

# Report
SIZE=$(du -sh "$ARCHIVE" | cut -f1)
echo "[$(date)] Backup created: $ARCHIVE ($SIZE)"

# Keep only the last 7 backups
ls -t "$BACKUP_DIR"/home-backup-*.tar.gz 2>/dev/null | tail -n +8 | xargs -r rm

BACKUP

chmod +x ~/bin/backup.sh

Test it:

~/bin/backup.sh
ls -lh ~/backups/

Part 8: Scheduled Tasks#

References: Module 12 — Scheduling

Schedule the backup to run daily:

crontab -e

and add

0 2 * * * $HOME/bin/backup.sh >> $HOME/backups/backup.log 2>&1

Part 9: Dotfiles Repository#

Version-control your configuration files so you can replicate your setup on any machine:

# Create a dotfiles repo
mkdir -p ~/dotfiles
cd ~/dotfiles

# Copy your configs
cp ~/.bashrc .bashrc
cp ~/.vimrc .vimrc
cp ~/.tmux.conf .tmux.conf
cp ~/bin/backup.sh backup.sh

# Create an install script
cat > install.sh << 'INSTALL'
#!/bin/bash
set -euo pipefail

DOTFILES_DIR="$(cd "$(dirname "$0")" && pwd)"

# Symlink dotfiles to home directory
ln -sf "$DOTFILES_DIR/.bashrc" ~/.bashrc
ln -sf "$DOTFILES_DIR/.vimrc" ~/.vimrc
ln -sf "$DOTFILES_DIR/.tmux.conf" ~/.tmux.conf

# Copy scripts
mkdir -p ~/bin
cp "$DOTFILES_DIR/backup.sh" ~/bin/backup.sh
chmod +x ~/bin/backup.sh

echo "Dotfiles installed. Run 'source ~/.bashrc' to apply."
INSTALL

chmod +x install.sh

# Initialize git repo
git init
git add -A
git commit -m "Initial dotfiles"

On a new machine, clone your dotfiles repo and run ./install.sh.


Part 10: System Health Check Script#

Combine your knowledge into a system overview script:

cat > ~/bin/sysinfo.sh << 'SYSINFO'
#!/bin/bash

echo "=== System Info ==="
echo "Hostname: $(hostname)"
echo "Kernel:   $(uname -r)"
echo "Uptime:   $(uptime -p)"
echo

echo "=== CPU ==="
echo "Cores: $(nproc)"
echo "Load:  $(cat /proc/loadavg | awk '{print $1, $2, $3}')"
echo

echo "=== Memory ==="
free -h | grep -E "Mem|Swap"
echo

echo "=== Disk ==="
df -h / /home 2>/dev/null | grep -v "^Filesystem" | awk '{print $6 ": " $3 "/" $2 " (" $5 " used)"}'
echo

echo "=== Network ==="
ip -br addr show | grep -v "^lo"
echo

echo "=== Listening Ports ==="
ss -tlnp 2>/dev/null | grep LISTEN | awk '{print $4}' | sort -u
echo

echo "=== Last 5 Logins ==="
last -5 -a 2>/dev/null || echo "(not available)"
echo

echo "=== Failed Services ==="
systemctl --failed --no-pager 2>/dev/null || echo "(none)"

SYSINFO

chmod +x ~/bin/sysinfo.sh

Test it:

~/bin/sysinfo.sh

Checklist#

Use this as a reference for what a well-configured Linux system should have:

  • System is up to date (sudo apt update && sudo apt upgrade)
  • Essential tools installed (git, vim, tmux, htop, curl)
  • Shell customized (.bashrc — aliases, prompt, history, functions)
  • Editor configured (.vimrc)
  • tmux configured (.tmux.conf)
  • SSH keys generated and public key distributed
  • SSH config set up for frequently accessed hosts
  • Firewall enabled with sensible defaults
  • fail2ban installed and running
  • Automatic security updates enabled (unattended-upgrades)
  • Backup script created and scheduled via cron
  • Dotfiles version-controlled in a git repo
  • System health script available

What’s Next?#

You now have the knowledge and tools to be a confident Linux power user. From here, you might explore:

  • Containers — Docker, Podman (lightweight isolated environments)
  • Configuration management — Ansible (automate server setup)
  • Cloud — AWS, GCP, Azure CLI tools (you will rack up generational debt)
  • Web servers — Nginx, Apache (host websites and services)
  • Databases — PostgreSQL, MySQL/MariaDB
  • Monitoring — Prometheus, Grafana (track system health)
  • Virtualization — KVM, libvirt (run virtual machines)

Review#

1. Why use a dotfiles repository?

It version-controls your configuration files so you can replicate your environment on any machine by cloning the repo and running an install script. It also tracks changes over time and makes it easy to share configurations.

2. Why use symlinks for dotfiles instead of copying?

Symlinks point to the files in your dotfiles repo. When you edit ~/.bashrc, you are editing the repo copy directly — changes are tracked by git without an extra copy step.

3. What should a basic firewall setup include?

Default deny incoming, default allow outgoing. Explicitly allow SSH (with rate limiting). Enable the firewall. Allow additional ports only as needed for services you run.

4. Why redirect cron job output to a log file?

Cron sends output via email by default. Without a mail server configured (common on desktops), output is lost. Redirecting to a log file ensures you can review output and debug issues.

5. What is the benefit of a system health check script?

A single command gives you a quick overview of system status — CPU, memory, disk, network, listening ports, failed services. It saves time compared to running multiple commands individually, and you can schedule it or run it immediately when troubleshooting.


Previous: Automatic Updates and Auditing


Congratulations! You have completed the Linux course. You now have the skills to navigate, configure, script, secure, and troubleshoot Linux systems with confidence.