Laptop, desktop or a VPS
A fresh install is not a finished install. One hour, done properly once — updates, the package managers nobody explains, a security baseline, backups that actually run, a certificate, and the housekeeping that stops the disk filling up six months from now.
# start here, every single time sudo apt update sudo apt upgrade -y sudo reboot
Read this first
All five matter within a week — and on a machine with a public address, within minutes.
ufw ships with Ubuntu and does nothing at all until you enable it.The checklist
Every step below applies to any Ubuntu machine unless its label says otherwise. Tell the page what you are setting up and it will show you only the steps that apply.
I am setting up:
Three commands and one reboot, before anything else.
# 1. bring the system fully up to date sudo apt update sudo apt upgrade -y # 2. firmware updates - laptops especially fwupdmgr refresh fwupdmgr get-updates fwupdmgr update # 3. reboot if a new kernel arrived ls /var/run/reboot-required && sudo reboot
apt update refreshes the package lists; it installs nothing on its ownapt upgrade is what actually installs the new versionsssh root@203.0.113.10 apt update && apt upgrade -y hostnamectl set-hostname web01 timedatectl set-timezone Asia/Bahrain ls /var/run/reboot-required && reboot
ip a — the public address matches the paneldf -h — how much disk you actually havefree -h — how much RAM, and whether swap existsss -tulpn — what is listening before you changed anythingcat /etc/os-release — the release you asked forName the host something you will recognise at 3 a.m., not server1. Thirty seconds of sanity checking confirms the machine you are paying for is the machine you ordered.
Most VPS images ship with no swap. On a 2 GB machine that is worth fixing before you run a database — a 2 GB swap file costs nothing and stops the out-of-memory killer ending your database at 3 a.m.
sudo fallocate -l 2G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
Six subcommands cover everything. Two of them are constantly confused with each other.
| apt update | Refreshes the list of available packages. Installs nothing |
| apt upgrade | Installs newer versions of what you already have |
| apt full-upgrade | Same, but will remove packages if needed to resolve dependencies |
| apt install NAME | Installs a package and its dependencies |
| apt remove NAME | Removes the package, keeps its config files |
| apt purge NAME | Removes the package and its config files |
| apt autoremove | Deletes dependencies nothing needs any more |
| apt search / show | Find a package, or read what it actually is |
update and upgrade are not the same thing. update asks "what is available?" and upgrade says "install it". Running upgrade without update first means upgrading against a stale list — which is why every guide chains them with &&.
Keep full-upgrade for release jumps rather than routine updating — letting apt remove a package is a decision worth making deliberately.
One command. Everything here earns its place within the first week.
sudo apt install -y \
build-essential git curl wget vim nano \
htop btop net-tools dnsutils traceroute \
tree unzip zip p7zip-full \
ufw gnupg ca-certificates \
tlp gnome-tweaks ubuntu-restricted-extras
| Build & dev | build-essential, git, curl, wget |
| Monitoring | htop, btop — see what is using the CPU |
| Network | net-tools, dnsutils, traceroute — ip, dig, ss |
| Archives | unzip, zip, p7zip-full — open anything |
| Security | ufw, gnupg, ca-certificates |
| Desktop | gnome-tweaks, restricted-extras (codecs, fonts) |
On a server, drop the last line — tlp, gnome-tweaks and the restricted extras are desktop packages.
Every action attributed, every mistake recoverable. Every guide from here on assumes it.
# create a user for yourself adduser deploy usermod -aG sudo deploy # give it your SSH key rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy # test it from a SECOND terminal ssh deploy@203.0.113.10 sudo whoami # -> root
Do not close the root window until you have logged in as the new user in a second terminal and confirmed sudo whoami returns root. This is the single most common way people lock themselves out of a brand new server.
Keys, three config lines and a firewall in the right order. The highest-value fifteen minutes of the whole build.
# on your own machine
ssh-keygen -t ed25519 -C 'you@laptop'
ssh-copy-id user@server
# then, on the server - the three lines sudo nano /etc/ssh/sshd_config PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes sudo sshd -t # test the config FIRST sudo systemctl reload ssh
sshd -t is not optional ceremony. It catches the typo that would otherwise lock you out the moment you reload.
# SSH first, then the web ports, then enable sudo ufw allow OpenSSH sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable sudo ufw status verbose
Running ufw enable without an SSH rule disconnects you immediately and permanently — the firewall denies inbound by default, including the session you are typing in. Recovery means the provider's web console. Every admin does this exactly once; do it on a lab machine rather than a real one.
Your provider's firewall — Hetzner, DigitalOcean, AWS security groups — sits in front of the machine, and ufw can neither see nor override it. If curl works on the server but not from your laptop, that outer layer is why. Use both: defence in depth costs nothing here.
# jail the ones knocking sudo apt install -y fail2ban sudo systemctl enable --now fail2ban sudo fail2ban-client status sshd # see what you are protected from sudo grep 'Failed password' /var/log/auth.log | wc -l
# security patches applied automatically sudo apt install -y unattended-upgrades sudo dpkg-reconfigure --priority=low unattended-upgrades systemctl status unattended-upgrades
unattended-upgrades belongs beside the firewall, not after it: a firewall does not help if the service behind it is months out of date.
The one task everybody postpones until the week after they needed it.
| A provider snapshot | File-level backup | |
|---|---|---|
| What it is | Whole-disk image of the machine | Versioned copies of the files you choose |
| Good for | Restoring the entire server in minutes, before a risky change | "I deleted one file yesterday", offsite copies, deduplication |
| Useless for | Getting one directory back without rolling the whole machine back | Bare-metal recovery of a broken upgrade |
Turn on the provider's automatic snapshots when you create the server — they usually cost about 20% and are the fastest possible recovery from a broken upgrade. Then add file backups for what snapshots cannot do.
# built in, graphical, good enough for a laptop sudo apt install -y deja-dup
# versioned, encrypted, scriptable sudo apt install -y restic restic init --repo /mnt/backup restic -r /mnt/backup backup /home/you /var/www /etc restic -r /mnt/backup snapshots restic -r /mnt/backup forget \ --keep-daily 7 --keep-weekly 4 --prune # dump the database, do not copy its live files mysqldump -u root -p --all-databases > dump.sql # and the step nobody does restic -r /mnt/backup restore latest --target /tmp/test
Put a restore in your calendar once a quarter. Restoring one random file proves the whole chain works — and it is the only proof that counts.
Ubuntu handles most hardware automatically. These are the exceptions.
| Additional Drivers | ubuntu-drivers devices Lists proprietary drivers Ubuntu can install for you — mostly NVIDIA and some Wi-Fi chips |
| NVIDIA graphics | sudo ubuntu-drivers autoinstall Installs the recommended driver. Reboot afterwards, then check with nvidia-smi |
| Firmware updates | fwupdmgr refresh && fwupdmgr update Vendor firmware for SSDs, docks, keyboards and laptops. Fixes bugs that look like Linux problems |
| Printers and scanners | CUPS is already running Most modern printers work over the network with no driver at all |
Wayland or X11? Ubuntu defaults to Wayland, which is smoother on most hardware. If you hit screen-sharing or NVIDIA oddities, pick "Ubuntu on Xorg" from the gear icon on the login screen — it is a per-session choice, not a permanent one.
Fifteen minutes of settings that you will feel every day afterwards.
| GNOME Tweaks | Window buttons, fonts, animations, startup apps |
| Extensions | Dash to Panel, Clipboard Indicator, Vitals — via extensions.gnome.org |
| Keyboard shortcuts | Bind a key to open the terminal. You will use it constantly |
| Night light and scaling | Settings > Displays. Fractional scaling for HiDPI screens |
| Workspaces | Super + Page Up / Page Down. The fastest habit to pick up |
| Default applications | Settings > Apps. Set browser, mail and terminal once |
Two shortcuts worth learning immediately: Super opens the activities overview, and Super + A shows every installed application. Almost nobody needs a desktop icon again after that.
Aliases and a prompt you can read save minutes every single day.
# ~/.bashrc - add at the end
alias ll='ls -alF'
alias ..='cd ..'
alias update='sudo apt update && sudo apt upgrade -y'
alias ports='sudo ss -tulpn'
alias myip='curl -s ifconfig.me'
export HISTSIZE=10000
export HISTCONTROL=ignoreboth:erasedups
source ~/.bashrc
~/bin to PATH and put your own scripts thereZsh with oh-my-zsh is popular and pretty. Bash is what every server you ever log into actually has. Learn bash first; add zsh on your own machine afterwards if you want it.
If the machine is for work, these four steps come before anything else.
# compilers and headers sudo apt install -y build-essential # git, configured properly sudo apt install -y git git config --global user.name 'Your Name' git config --global user.email 'you@example.com' git config --global init.defaultBranch main # an SSH key for GitHub or GitLab ssh-keygen -t ed25519 -C 'you@example.com' cat ~/.ssh/id_ed25519.pub
# Docker, the official way curl -fsSL https://get.docker.com | sudo sh sudo usermod -aG docker $USER newgrp docker docker run hello-world # languages: use a version manager, not apt # node -> nvm # python -> pyenv or python3-venv # rust -> rustup
Do not install Node or Python versions from apt if you develop against specific versions. Version managers let each project use what it needs, and they never fight the system packages that Ubuntu itself depends on.
Adding yourself to the docker group is what stops you prefixing every container command with sudo. It normally applies at your next login — newgrp docker is what makes it apply now.
Two records and one wait. This is the step that turns an IP address into something people can use — and the one certbot depends on.
| Type | Name | Value | Notes |
|---|---|---|---|
| A | @ | 203.0.113.10 | The root domain |
| A | www | 203.0.113.10 | Or a CNAME to the root — either works |
| AAAA | @ | 2a01:4f8::1 | Only if your VPS has IPv6 — most do |
| CNAME | blog | example.com. | Subdomains pointing at the same host |
dig +short example.com # what the world sees dig @1.1.1.1 example.com # bypass your local resolver curl -I http://example.com # does it reach your server?
Set the TTL low — 300 seconds — before you change anything, and raise it once things are stable. A mistake then costs five minutes instead of a day. Propagation is usually minutes, occasionally hours.
Ten minutes from a bare server to a padlock in the address bar.
sudo apt install -y nginx systemctl status nginx # your site's files sudo mkdir -p /var/www/example.com/html echo '<h1>It works.</h1>' | \ sudo tee /var/www/example.com/html/index.html sudo nano /etc/nginx/sites-available/example.com sudo ln -s /etc/nginx/sites-available/example.com \ /etc/nginx/sites-enabled/ sudo rm /etc/nginx/sites-enabled/default sudo nginx -t && sudo systemctl reload nginx
sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d example.com -d www.example.com # certbot edits the config and reloads nginx for you sudo certbot certificates sudo certbot renew --dry-run systemctl list-timers | grep certbot
Ninety-nine times out of a hundred it is DNS not pointing here yet, or port 80 blocked in one of the two firewalls — the machine's or the provider's. Fix those before you touch certbot.
Renewal is a systemd timer certbot installs for you. renew --dry-run is how you confirm today that it will still work in three months — and an expiring certificate is one of the few things worth alerting on.
So you find out before a customer does.
# the four numbers that matter
df -h && free -h && uptime
systemctl --failed
# a dashboard in ten minutes curl -fsSL https://get.netdata.cloud/kickstart.sh | sh # do not expose it to the internet sudo ufw allow from YOUR.IP.ADDRESS to any port 19999
Layer a free external uptime check on top — UptimeRobot, Better Stack or Healthchecks.io. A check that runs on the machine cannot tell you the machine is down.
Laptops
Ubuntu is good on laptops now. Three settings make it noticeably better.
| TLP | sudo apt install -y tlp && sudo tlp start Automatic power tuning. Usually worth 30–60 minutes of battery on its own |
| powertop | sudo powertop --auto-tune Finds the components keeping the CPU awake. Run it once and read the tunables tab |
| Power mode | Settings > Power > Power Saver Built into GNOME, works with the firmware. Balanced is the sane default on AC |
| Swappiness | sudo sysctl vm.swappiness=10 Less aggressive swapping on a machine with 8 GB or more. Make it permanent in /etc/sysctl.conf |
Check what is actually draining the battery before you tune anything: powertop shows the top offenders, and it is often a browser tab rather than the operating system.
Housekeeping
Five commands, once a month. Skipping them is why people think Linux "gets slow".
# where has the space gone? df -h du -h --max-depth=1 /var | sort -rh | head # remove packages nothing needs any more sudo apt autoremove --purge sudo apt clean
# the journal grows forever by default journalctl --disk-usage sudo journalctl --vacuum-time=2weeks # snaps keep old revisions snap list --all sudo snap set system refresh.retain=2
sudo apt update && sudo apt upgradesudo apt autoremove --purgejournalctl --vacuum-time=2weeksWhen something is off
Almost all of them have a one-line answer.
| Symptom | Most likely cause | Fix |
|---|---|---|
| Locked out right after ufw enable | No SSH rule existed when the firewall came up | The provider's web console; allow OpenSSH first next time |
| curl works on the server, not from outside | The provider's cloud firewall in front of it | Open the port there too — ufw cannot |
| certbot validation fails | DNS not pointing here, or port 80 blocked | Fix those two; the problem is not certbot |
| No sound, or the wrong output | Wrong default device | Settings > Sound, pick the output |
| Screen tearing or stutter | Graphics driver or Wayland | ubuntu-drivers autoinstall, or try Xorg |
| Wi-Fi drops on resume | Power saving on the wireless card | Disable power_save in NetworkManager |
| "Could not get lock" from apt | Another update is already running | Wait, or check for unattended-upgrades |
| Snap apps start slowly | First launch decompresses the snap | Use the apt version where one exists |
| Disk filling up quickly | Journal, snaps or old kernels | Vacuum the journal, apt autoremove |
| No permission on a USB drive | Ownership of the mount | Check with ls -l, then chown or mount options |
| Fonts look wrong in one app | Missing Microsoft fonts | sudo apt install ubuntu-restricted-extras |
And the general rule: read the actual error before searching for it. Ubuntu's messages are usually specific, and the first line is almost always the useful one.
Keep this page
Everything from this guide, grouped by when you need it. You will not remember it all on day one — you only need to know where to look.
sudo apt update && sudo apt upgrade -y sudo apt full-upgrade fwupdmgr refresh && fwupdmgr update ls /var/run/reboot-required
sudo apt install -y NAME sudo apt purge NAME apt search KEYWORD apt show NAME
ssh-keygen -t ed25519 ssh-copy-id user@server sudo sshd -t sudo systemctl reload ssh
sudo ufw allow OpenSSH sudo ufw allow 80/tcp && sudo ufw allow 443/tcp sudo ufw enable sudo ufw status verbose
dig +short example.com dig @1.1.1.1 example.com curl -I http://example.com
sudo certbot --nginx -d example.com sudo certbot certificates sudo certbot renew --dry-run systemctl list-timers | grep certbot
ubuntu-drivers devices sudo ubuntu-drivers autoinstall nvidia-smi lspci -nn | grep -i net
restic init --repo /mnt/backup restic -r /mnt/backup backup /home/you restic -r /mnt/backup snapshots restic -r /mnt/backup restore latest
sudo apt autoremove --purge sudo apt clean journalctl --vacuum-time=2weeks df -h && du -h -d1 /var