System administration covers everything from monitoring resources and managing packages to scheduling jobs and managing the environment. These are the day-to-day operations of keeping a Linux system running well.
# df — disk free (filesystem-level view)
$ df -h # human-readable (K, M, G)
$ df -hT # include filesystem type
$ df -h /home # specific filesystem
$ df -i # inode usage (number of files)
$ df -h --total # grand total line
# Example output:
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 50G 18G 29G 39% /
/dev/sda2 200G 145G 45G 77% /home
tmpfs 7.8G 1.2M 7.8G 1% /run
# du — disk usage (directory-level view)
$ du -sh /var/log # total size of /var/log
$ du -sh * # size of each item in current dir
$ du -sh /* 2>/dev/null # size of each root-level dir
$ du -h /var/log | sort -h # sort by size
$ du -sh /var/* | sort -h # all dirs under /var, sorted
$ du -d 1 -h /var/ # depth limit: 1 level deep
$ du --max-depth=2 -h /home # depth limit: 2 levels
$ du -h --exclude='*.mp4' /home # exclude certain files
# Find largest files
$ find /var -type f -size +50M -exec ls -lh {} \; | sort -k5 -h
# Disk health
$ sudo fdisk -l # list all disk partitions
$ sudo lsblk # block device tree
$ sudo lsblk -f # with filesystem info
$ sudo blkid # block device UUIDs and types
$ sudo smartctl -a /dev/sda # SMART disk health (install: apt install smartmontools)
# free — memory information
$ free -h # human-readable
$ free -m # in megabytes
$ free -s 2 # update every 2 seconds
$ free -h --si # use 1000 instead of 1024 as base
# Example output:
total used free shared buff/cache available
Mem: 15Gi 6.2Gi 1.8Gi 512Mi 7.1Gi 8.7Gi
Swap: 2.0Gi 0B 2.0Gi
# "available" is what can actually be given to new processes
# buff/cache is used by OS for disk caching (can be freed if needed)
# Detailed memory info
$ cat /proc/meminfo # raw kernel memory statistics
# vmstat — virtual memory stats
$ vmstat 1 5 # report every 1 second, 5 times
$ vmstat -s # summary statistics
# CPU information
$ lscpu # CPU details
$ cat /proc/cpuinfo # raw CPU info
$ nproc # number of processing units
# Kernel and OS version $ uname -a # all system info $ uname -r # kernel version only $ uname -m # machine hardware (x86_64, aarch64) $ lsb_release -a # distribution info (Debian/Ubuntu) $ cat /etc/os-release # distribution info (all distros) $ cat /etc/debian_version # Debian version $ cat /etc/redhat-release # RHEL/CentOS version # System uptime and load $ uptime 10:23:45 up 15 days, 2:14, 2 users, load average: 0.52, 0.58, 0.65 # up X days = time since last reboot # load average = 1/5/15 minute CPU load (1.0 per core = 100% on that core) $ uptime -p # pretty format: up 15 days, 2 hours, 14 minutes $ uptime -s # when system started: 2024-06-10 08:09:31 # Who is logged in $ w # who + what they're doing + load $ who # who is logged in $ last # login history $ last reboot # last reboot times
# apt — the front-end package manager # Update package index (always do this first) $ sudo apt update # refresh package list $ sudo apt update && sudo apt upgrade # update everything # Install packages $ sudo apt install nginx # install nginx $ sudo apt install nginx php-fpm mysql-server # multiple packages $ sudo apt install -y nginx # auto-yes (no prompts) $ sudo apt install --no-install-recommends nginx # minimal install # Remove packages $ sudo apt remove nginx # remove but keep config $ sudo apt purge nginx # remove + delete config files $ sudo apt autoremove # remove orphaned dependencies $ sudo apt purge --autoremove nginx # remove + config + orphans # Upgrade $ sudo apt upgrade # upgrade installed packages (safe) $ sudo apt full-upgrade # upgrade including removals (more thorough) $ sudo apt dist-upgrade # Debian-style dist upgrade # Search and info $ apt search nginx # search for packages $ apt show nginx # show package details $ apt list --installed # list installed packages $ apt list --upgradable # packages with updates available $ dpkg -l # list all installed (detailed) $ dpkg -l | grep nginx # is nginx installed? $ dpkg -L nginx # files installed by nginx package $ dpkg -S /usr/sbin/nginx # which package owns this file? # Cache management $ sudo apt clean # clear download cache $ sudo apt autoclean # remove old downloaded packages $ du -sh /var/cache/apt/archives # how big is the cache # Hold/unhold package version $ sudo apt-mark hold nginx # prevent nginx from being upgraded $ sudo apt-mark unhold nginx # allow upgrades again $ apt-mark showhold # list held packages
# dnf — modern yum replacement (RHEL 8+, Fedora, CentOS 8+) # yum — still available on older CentOS/RHEL 7 $ sudo dnf update # update all packages $ sudo dnf upgrade # same as update (aliases) $ sudo dnf install nginx # install package $ sudo dnf install nginx php-fpm mariadb-server $ sudo dnf remove nginx # remove package $ sudo dnf autoremove # remove unused dependencies # Search and info $ dnf search nginx # search packages $ dnf info nginx # package details $ dnf list installed # list installed packages $ dnf list available | grep nginx # search available $ rpm -qa # list all installed (raw) $ rpm -qi nginx # package info $ rpm -ql nginx # files in package $ rpm -qf /usr/sbin/nginx # which package owns file # Groups $ dnf group list # list package groups $ dnf group install "Development Tools" # install a group # Repos $ dnf repolist # list enabled repos $ dnf config-manager --add-repo URL # add repo # EPEL — extra packages for enterprise Linux $ sudo dnf install epel-release # enable EPEL repo
# Snap — Canonical's universal package format (Ubuntu default) $ snap list # installed snaps $ snap find firefox # search snap store $ sudo snap install firefox # install $ sudo snap remove firefox # remove $ sudo snap refresh # update all snaps $ sudo snap refresh firefox # update specific snap # Flatpak — Red Hat's universal format $ flatpak list # installed flatpaks $ flatpak search gimp # search $ flatpak install flathub org.gimp.GIMP # install from Flathub $ flatpak run org.gimp.GIMP # run $ flatpak uninstall org.gimp.GIMP $ flatpak update # update all
# tar — the Linux archiving standard # Syntax: tar [operation][modifiers] archive files # Create archives $ tar -czf archive.tar.gz /path/to/dir/ # create gzip compressed archive $ tar -cjf archive.tar.bz2 /path/to/dir/ # create bzip2 archive (smaller, slower) $ tar -cJf archive.tar.xz /path/to/dir/ # create xz archive (best compression) $ tar -cf archive.tar /path/to/dir/ # create without compression $ tar -czf backup_$(date +%Y%m%d).tar.gz /etc/ # timestamped backup # Extract archives $ tar -xzf archive.tar.gz # extract gzip archive $ tar -xjf archive.tar.bz2 # extract bzip2 $ tar -xJf archive.tar.xz # extract xz $ tar -xf archive.tar.gz # auto-detect compression $ tar -xzf archive.tar.gz -C /tmp/ # extract to specific directory $ tar -xzf archive.tar.gz file.txt # extract single file # List archive contents $ tar -tzf archive.tar.gz # list gzip archive $ tar -tf archive.tar.gz # list (auto-detect) # Flags reference # c = create, x = extract, t = list, u = update # z = gzip, j = bzip2, J = xz # f = file (must be last before filename) # v = verbose (show files as processed) # C = change to directory # zip/unzip $ zip archive.zip file1 file2 file3 # create zip $ zip -r archive.zip directory/ # recursive zip $ zip -e secure.zip sensitive_file # encrypted zip $ unzip archive.zip # extract $ unzip archive.zip -d /tmp/ # extract to directory $ unzip -l archive.zip # list contents $ unzip -t archive.zip # test integrity # gzip/gunzip — single file compression $ gzip file.txt # compress (creates file.txt.gz, removes original) $ gzip -k file.txt # keep original $ gzip -d file.txt.gz # decompress $ gunzip file.txt.gz # same as gzip -d $ gzip -l file.txt.gz # show compression ratio $ zcat file.txt.gz # read without decompressing
# View environment $ env # all environment variables $ printenv # same $ printenv PATH # specific variable $ echo $HOME # print variable value $ echo $USER $SHELL $PWD # multiple variables # Important environment variables # $PATH — directories searched for commands # $HOME — current user's home directory # $USER — current username # $SHELL — current shell binary path # $EDITOR — default text editor # $LANG — locale setting # $TERM — terminal type # $PS1 — command prompt format # $DISPLAY — X11 display (for GUI apps) # Set variables $ MY_VAR="hello" # set in current shell only $ export MY_VAR="hello" # export to child processes $ export PATH="$PATH:/opt/myapp/bin" # add to PATH # Unset variables $ unset MY_VAR # Set variable for single command only $ EDITOR=vim crontab -e # use vim for this command only $ DEBUG=true ./script.sh # pass variable to script # Shell configuration files (load order matters!) # ~/.bashrc — interactive non-login shells (most daily use) # ~/.bash_profile — login shells (SSH connections, TTY login) # /etc/environment — system-wide, all processes # /etc/profile — system-wide login shells # /etc/profile.d/*.sh — modular profile scripts # Edit ~/.bashrc to persist settings $ nano ~/.bashrc # Add these lines: export EDITOR=vim export PATH="$PATH:$HOME/.local/bin" alias ll='ls -lah' alias gs='git status' # Apply changes to current session $ source ~/.bashrc $ . ~/.bashrc # same (dot command)
# crontab — per-user cron schedule $ crontab -e # edit your crontab (opens in $EDITOR) $ crontab -l # list your current crontab $ crontab -r # remove your crontab (careful!) $ sudo crontab -e -u alice # edit alice's crontab (as root) $ crontab -l -u alice # list alice's crontab # Cron syntax: minute hour day-of-month month day-of-week command # ┌───────────── minute (0-59) # │ ┌───────────── hour (0-23) # │ │ ┌───────────── day of month (1-31) # │ │ │ ┌───────────── month (1-12 or Jan-Dec) # │ │ │ │ ┌───────────── day of week (0-7, 0 and 7 = Sunday) # │ │ │ │ │ # * * * * * command-to-execute # Cron examples 0 2 * * * /usr/bin/backup.sh # daily at 2:00 AM */15 * * * * /usr/bin/check_status.sh # every 15 minutes 0 0 * * 0 /usr/bin/weekly_report.sh # every Sunday midnight 0 */4 * * * /usr/bin/sync_data.sh # every 4 hours 30 8 1 * * /usr/bin/monthly_invoice.sh # 1st of month at 8:30 0 9-17 * * 1-5 /usr/bin/business_hours_check.sh # hourly Mon-Fri 9am-5pm @reboot /usr/bin/startup_script.sh # at system boot @daily /usr/bin/daily_cleanup.sh # shorthand for 0 0 * * * @weekly /usr/bin/weekly_backup.sh # 0 0 * * 0 @monthly /usr/bin/monthly_report.sh # 0 0 1 * * # System-wide cron $ ls /etc/cron.d/ # system cron jobs $ ls /etc/cron.daily/ # scripts run daily $ ls /etc/cron.weekly/ # scripts run weekly $ ls /etc/cron.hourly/ # scripts run hourly # Redirect cron output to avoid email 0 2 * * * /usr/bin/backup.sh >> /var/log/backup.log 2>&1 # at — run a one-time job at a specific time $ at 14:30 # schedule for 2:30 PM at> /usr/bin/report.sh at> Ctrl+D $ at now + 2 hours # 2 hours from now $ at midnight tomorrow # midnight tonight $ atq # list pending jobs $ atrm 3 # remove job number 3
# Alias — shorthand for commands # Temporary (current session only) $ alias ll='ls -lah' $ alias gs='git status' $ alias la='ls -A' $ alias cls='clear' $ alias ..='cd ..' $ alias ...='cd ../..' $ alias grep='grep --color=auto' $ alias df='df -h' $ alias du='du -h' $ alias please='sudo' # View aliases $ alias # list all aliases $ alias ll # show specific alias # Remove an alias $ unalias ll # Make permanent: add to ~/.bashrc $ cat >> ~/.bashrc << 'EOF' # Custom aliases alias ll='ls -lah' alias la='ls -A' alias l='ls -CF' alias ..='cd ..' alias ...='cd ../..' alias grep='grep --color=auto' alias df='df -h' alias du='du -h' alias free='free -h' alias update='sudo apt update && sudo apt upgrade' alias ports='ss -tlnp' alias myip='curl -s ifconfig.me' alias reload='source ~/.bashrc' EOF $ source ~/.bashrc
# Symbolic links (symlinks) — like a shortcut/pointer $ ln -s /path/to/original /path/to/link # create symlink $ ln -s /opt/nginx-1.24/bin/nginx /usr/local/bin/nginx $ ln -s /data/logs/app.log /var/log/app.log $ ln -sf /path/to/new_target existing_link # force (overwrite existing link) # View symlinks $ ls -la /usr/local/bin/nginx lrwxrwxrwx 1 root root 25 Jun 15 /usr/local/bin/nginx -> /opt/nginx-1.24/bin/nginx $ readlink /usr/local/bin/nginx # show link target $ readlink -f /usr/local/bin/nginx # show final resolved path # Hard links — both names point to same inode (same data on disk) $ ln original.txt hardlink.txt # create hard link $ ls -li original.txt hardlink.txt # same inode number! # Deleting one doesn't affect the other # Hard links cannot cross filesystems or link directories # Difference: symlink vs hard link # Symlink: separate file pointing to path (breaks if target moves/deleted) # Hard link: another name for same inode (survives if other name deleted) # Remove a symlink (DON'T use rm -r, it follows the link) $ rm /usr/local/bin/nginx # remove symlink $ unlink /usr/local/bin/nginx # also works
# mount — attach filesystems $ mount # show all mounted filesystems $ mount | grep /dev/sd # show disk mounts $ sudo mount /dev/sdb1 /mnt # mount device to mount point $ sudo mount -t ext4 /dev/sdb1 /mnt # specify filesystem type $ sudo mount -o ro /dev/sdb1 /mnt # mount read-only $ sudo mount -o remount,rw / # remount root as read-write $ sudo mount -t tmpfs -o size=512m tmpfs /mnt/ram # RAM filesystem $ sudo umount /mnt # unmount (when not in use) $ sudo umount -l /mnt # lazy unmount (safe if busy) # /etc/fstab — auto-mount at boot $ cat /etc/fstab # <device> <mountpoint> <fstype> <options> <dump> <pass> UUID=abc-123 / ext4 errors=remount-ro 0 1 UUID=def-456 /home ext4 defaults 0 2 UUID=ghi-789 swap swap sw 0 0 /dev/sdb1 /data xfs defaults,nofail 0 2 192.168.1.5:/share /mnt/nfs nfs defaults 0 0 # Use UUID (not /dev/sdX) because device names can change on reboot $ blkid /dev/sdb1 # get UUID $ sudo mount -a # mount everything in fstab $ sudo mount -o remount /data # remount with fstab options # NFS mount $ sudo apt install nfs-common $ sudo mount -t nfs 192.168.1.5:/share /mnt/nfs # CIFS/SMB mount (Windows share) $ sudo apt install cifs-utils $ sudo mount -t cifs //server/share /mnt/smb -o user=alice,password=secret
systemd timers are a modern alternative to cron with better logging and dependency management.
# Create a timer unit $ sudo vim /etc/systemd/system/daily-backup.service
[Unit] Description=Daily Backup Service [Service] Type=oneshot ExecStart=/usr/local/bin/backup.sh User=backupuser
$ sudo vim /etc/systemd/system/daily-backup.timer
[Unit] Description=Run daily backup at 2am Requires=daily-backup.service [Timer] OnCalendar=*-*-* 02:00:00 Persistent=true [Install] WantedBy=timers.target
$ sudo systemctl daemon-reload $ sudo systemctl enable --now daily-backup.timer $ systemctl list-timers # list all timers and next run time $ systemctl list-timers --all # include inactive timers $ journalctl -u daily-backup.service # view backup logs