Rewrite the Debian installation article
This commit is contained in:
@@ -64,6 +64,11 @@ export default defineAppConfig({
|
||||
'directory tree': 'i-lucide-folder-tree',
|
||||
'ini': 'i-lucide-settings',
|
||||
'conf': 'i-lucide-settings',
|
||||
'service': 'i-lucide-settings',
|
||||
// Used as ::code-group tab labels when a command differs per OS.
|
||||
'macos': 'i-simple-icons-apple',
|
||||
'linux': 'i-simple-icons-linux',
|
||||
'windows': 'i-simple-icons-windows',
|
||||
},
|
||||
pre: {
|
||||
slots: {
|
||||
|
||||
@@ -26,3 +26,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* A screenshot inside a list item (the step-by-step install guides) is
|
||||
rendered as a bare <img> child of the <li> and gets no margin at all, so it
|
||||
ends up glued to the text above and below it. The same image in a paragraph
|
||||
is wrapped in a <p> that carries the prose spacing. Give it that spacing
|
||||
back so illustrated steps stay readable. */
|
||||
li > img {
|
||||
margin-block: 1.25rem;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,39 +5,337 @@ description: Step-by-step guide to install Debian 13 on a home server and set up
|
||||
|
||||
|
||||
:ellipsis{left=0px width=40rem top=10rem blur=140px zIndex=60}
|
||||
# Debian 13
|
||||
::note
|
||||
🎯 __Goal:__ Install Debian 13 and the main dependencies to have a ready-to-use OS, accessible via SSH.
|
||||
::
|
||||
|
||||
[Debian 13 "Trixie"](https://www.debian.org/) is the base this whole guide sits on. It's a plain, boring, stable Linux, and for a homelab that's exactly the point: package versions stay frozen for the life of the release, security patches keep coming for about five years counting LTS, so the machine keeps running for years instead of needing a rebuild every few months.
|
||||
|
||||
The other reason is that it stays out of your way. Appliance systems like Unraid, TrueNAS or Synology's DSM put their own layer on top of Docker, and the day you need something their interface doesn't expose, you're stuck working around it. Debian is just a server: you install what you want, where you want, and nothing is hidden behind someone else's UI. It also happens to be what most self-hosted projects target first, so their docs hand you `apt` commands that work as-is, Docker publishes an official Debian repository, and any error message you paste into a search engine already has years of answers behind it. A minimal install is light enough to leave nearly all the RAM and CPU of a small N100 box to your containers.
|
||||
|
||||

|
||||
|
||||
## Installation
|
||||
|
||||
1. [BIOS Setup](https://www.debian.org/releases/stable/i386/ch03s06.en.html#bios-setup)
|
||||
2. [Download Debian Image](https://www.debian.org/download.en.html)
|
||||
3. [Create Bootable USB (Rufus)](https://dev.to/devops2808/how-to-create-bootable-usb-installer-for-debian-12-4f66)
|
||||
4. [Install Debian and Set Up SSH](https://www.howtoforge.com/tutorial/debian-minimal-server/)
|
||||
5. Install sudo and add a user to the sudo group for administrative privileges.
|
||||
Log in as root:
|
||||
::note{icon="" to="https://www.iamtimsmith.com/blog/getting-started-with-the-linux-cli" target="_blank"}
|
||||
📋 __Prerequisite:__ everything past this point happens in a terminal, so you should be comfortable with the basics: moving around with `cd`, listing with `ls`, editing a file with `nano`, and reading what a command tells you when it fails. If any of that is new, **start with the Linux CLI** and come back.
|
||||
::
|
||||
|
||||
### BIOS setup
|
||||
|
||||
Press :kbd{value="Del"} or :kbd{value="F2"} right after powering on to open the firmware setup (the boot screen usually says which key it is). Most machines also have a one-shot boot menu, often :kbd{value="F12"}, :kbd{value="F11"} or :kbd{value="F8"}, which lets you boot the USB installer once without touching the permanent boot order. Debian documents the general procedure in its [installation manual](https://www.debian.org/releases/forky/amd64/ch03s06.en.html), and here is what matters before you install:
|
||||
|
||||
- **Boot mode.** Prefer native UEFI. The important part is that the installer boots in the *same* mode you intend to run the server in, because UEFI uses GPT partitioning while legacy BIOS (and UEFI in CSM mode) uses a DOS partition table, and a mismatch installs the bootloader in the wrong place. Watch out on multi-boot machines: the default boot mode for removable devices is often not the one used for internal disks.
|
||||
- **Secure Boot** can stay enabled. Debian ships a Microsoft-signed shim bootloader, so it boots fine as-is.
|
||||
- **SATA mode** should be `AHCI`, not `RAID` / Intel RST, otherwise Linux may not see your drives at all. Changing this after installing another OS on the same disk will stop that OS from booting, so set it before you start.
|
||||
- **Restore on AC power loss** so the server comes back by itself after an outage instead of waiting for someone to press the button. The setting lives in *Power Management*, *APM Configuration* or *Advanced* depending on the board, under a name like *Restore on AC Power Loss*, *AC Power Recovery*, *After Power Failure*, *AC Back Function* or *S0 state*. Set it to **Power On**, not *Last State*, which would leave the machine off if the outage caught it during a shutdown.
|
||||
- **Wake on LAN**, if you want to power the machine up remotely instead of walking to it. Same *Power Management* menu: set *Wake on LAN*, *Power On By PCI-E/PCI* or *Resume by PCI-E Device* to **Enabled**, then disable *ErP* / *EuP Ready* and *Deep Sleep* / *Deep Sx*, which cut power to the network card once the machine is off and would keep it deaf to the magic packet. Debian also needs the card told to listen, see [Going further](#going-further).
|
||||
- **Virtualization** (`VT-x` / `AMD-V`, plus `VT-d` for passthrough) costs nothing to turn on now and saves a trip back into the BIOS the day you want to run a VM. Docker itself doesn't need it on Linux.
|
||||
|
||||
::note
|
||||
If you're dual-booting Windows, disable its *fast startup*: it leaves the filesystem in a state Linux can corrupt, and Windows Update likes to silently turn it back on.
|
||||
::
|
||||
|
||||
### Download the ISO and write it to a USB stick
|
||||
|
||||
::steps{level="4"}
|
||||
#### Download the netinst image
|
||||
|
||||
Grab the `amd64` **netinst** image from [debian.org](https://www.debian.org/download.en.html). It's around 700 MB and pulls the rest of the packages from the network during install, which is what you want on a server that's plugged into ethernet: you get current packages instead of installing from a months-old snapshot and patching afterwards. The full DVD images only make sense if the machine has no network during setup.
|
||||
|
||||
#### Write it with Rufus
|
||||
|
||||
On Windows, write it with [Rufus](https://rufus.ie/) (portable, no install needed). Plug in a USB stick of 2 GB or more, keeping in mind **it will be wiped entirely**, then:
|
||||
|
||||
- **Device**: your USB stick. Check the capacity twice, Rufus happily writes to the wrong drive if you let it.
|
||||
- **Boot selection**: `SELECT`, then pick the Debian ISO you just downloaded.
|
||||
- **Partition scheme**: this has to match the boot mode you set in the BIOS above. `GPT` for UEFI, `MBR` only if you're staying on legacy/CSM. The target system field follows automatically.
|
||||
- Leave the format options at their defaults, then hit `START`. If Rufus asks how to write the image, keep the recommended *ISO Image mode*.
|
||||
|
||||

|
||||
|
||||
_Screenshot from [this bootable USB guide on DEV Community](https://dev.to/devops2808/how-to-create-bootable-usb-installer-for-debian-12-4f66)._
|
||||
|
||||
Writing takes a few minutes.
|
||||
|
||||
#### Done !
|
||||
::
|
||||
|
||||
### Install Debian
|
||||
|
||||
Boot the USB stick (one-shot boot menu from the BIOS section) and pick **Install**, the text installer. The goal here is a minimal headless server: no desktop, no graphical session, nothing but a shell reachable over SSH. The screen and keyboard you're using right now are only needed for this one install, after that the machine runs blind in a corner. The [official installation guide](https://www.debian.org/releases/trixie/amd64/ch06s03.en.html) documents every screen.
|
||||
|
||||

|
||||
|
||||
::steps{level="4"}
|
||||
#### Language, country, keyboard
|
||||
|
||||
Nothing special. The keyboard layout is the one you're physically typing on, which is easy to get wrong if you picked English but type on AZERTY.
|
||||
|
||||
#### Network and hostname
|
||||
|
||||
A wired connection gets configured over DHCP by itself. When it asks for a **hostname**, give the machine a real name (`serveex`, `nas`...), you'll see it in every SSH prompt afterwards. The **domain** can be left empty, or set to something like `lan` if you already use one at home.
|
||||
|
||||

|
||||
|
||||
#### Root password and user account
|
||||
|
||||
Leave the **root password empty**. Debian then disables the root account, installs `sudo` and puts your user in it, which is the safer default and saves you a round of setup later.
|
||||
|
||||
Then create your user: full name, username, password. This is the account you'll SSH into. Avoid `admin` as a username, it's reserved on Debian and the installer will reject it.
|
||||
|
||||
#### Clock
|
||||
|
||||
Confirm the timezone guessed from your country.
|
||||
|
||||
#### Partitioning
|
||||
|
||||
*Guided, use entire disk* on the system drive, then *All files in one partition*, which gives you one big `/` plus a swap partition. Separate `/home` or `/var` partitions buy you very little here and mostly guarantee that one fills up while the others sit half empty. Pick LVM only if you already know you want snapshots or to grow volumes later. Your data disks are not touched at this stage, you'll mount them afterwards.
|
||||
|
||||
Finish with *Finish partitioning and write changes to disk*, then confirm with *Yes*: this is the point of no return for that disk.
|
||||
|
||||

|
||||
|
||||
#### Mirror and surveys
|
||||
|
||||
Answer *No* to *Scan another installation medium?*, everything else comes from the network. For the mirror, pick any one in your country, or `deb.debian.org` which routes to a nearby one automatically, and leave the HTTP proxy field empty unless you actually have one. The popularity contest (anonymous package statistics) is yes or no, no consequence either way.
|
||||
|
||||
#### Software selection (tasksel)
|
||||
|
||||
The screen that actually decides whether your server stays minimal. Uncheck **everything**, in particular `Debian desktop environment` and `GNOME`, which are ticked by default and would drag in gigabytes of packages plus a graphical session you will never display. Keep exactly two boxes: **`SSH server`**, your only way in from now on, and **`standard system utilities`**, which the rest of this guide assumes.
|
||||
|
||||
::warning
|
||||
Boxes are ticked and unticked with :kbd{value="Space"}, never :kbd{value="Enter"}. :kbd{value="Enter"} validates the whole screen and moves on, so pressing it on the desktop entry installs GNOME instead of removing it, and you get a graphical server you then have to strip by hand. Use :kbd{value="Tab"} to reach `Continue` once the two boxes above are the only ones checked.
|
||||
::
|
||||
|
||||

|
||||
|
||||
#### GRUB
|
||||
|
||||
Install it on the disk you just partitioned (`/dev/sda`, `/dev/nvme0n1`...), not on a partition.
|
||||
|
||||

|
||||
|
||||
#### Done !
|
||||
::
|
||||
|
||||
::note
|
||||
Before rebooting, take a minute to give the server a **fixed address** in your router. Everything that comes later points at it: your SSH shortcuts, the reverse proxy, the bookmarks to each service. On a plain DHCP lease that address changes on its own eventually and all of it breaks at once.
|
||||
|
||||
The clean way is a DHCP reservation, which ties the address to the server's MAC address while leaving the router in charge of the addressing. See [NAT & DHCP](/general/networking/nat) for where to find it in your router's interface.
|
||||
::
|
||||
|
||||
_Installer screenshots from [howtoforge.com's Debian minimal server guide](https://www.howtoforge.com/tutorial/debian-minimal-server/)._
|
||||
|
||||
### Connect over SSH
|
||||
|
||||
The server has no screen from now on, everything goes through SSH. These steps get you in, then make sure nobody else can be.
|
||||
|
||||
::steps{level="4"}
|
||||
#### Connect from another machine
|
||||
|
||||
Remove the USB stick and reboot. The address to use is the one you reserved in the router just before, `192.168.1.42` in the examples below.
|
||||
|
||||
Everything from here happens from another machine on your local network, not on the server. Windows and macOS both ship an SSH client, so there's nothing to install: open **PowerShell** on Windows, or **Terminal** on macOS, and type the same command.
|
||||
|
||||
```bash [Terminal]
|
||||
su -
|
||||
```
|
||||
Enter your password, then type:
|
||||
```bash [Terminal]
|
||||
apt install sudo
|
||||
```
|
||||
Add the user to the sudo group:
|
||||
```bash [Terminal]
|
||||
adduser <username> sudo
|
||||
ssh [email protected]
|
||||
```
|
||||
|
||||
Next time the user logs in, they will be able to use the `sudo` command to execute commands with administrative privileges.
|
||||
The first connection asks you to confirm the server's fingerprint, which is normal, answer `yes`. It gets stored in `~/.ssh/known_hosts` and you won't be asked again.
|
||||
|
||||
6. [Everything About Remote Console Access (SSH)](https://www.digitalocean.com/community/tutorials/ssh-essentials-working-with-ssh-servers-clients-and-keys)
|
||||
7. Optional - [UPS Client in Case of Power Outage](https://www.sindastra.de/p/2078/how-to-connect-linux-server-to-synology-ups-server) / [also here](https://www.reddit.com/r/synology/comments/gtkjam/use_synology_nas_as_ups_server_to_safely_power/)
|
||||
8. Optional - Wake up after power outage → configure BIOS S0 state
|
||||
9. Optional - [Wake Server Remotely (WoW - WoL)](https://dev.to/zakery1369/enable-wake-on-lan-on-debian-4ljd)
|
||||
::note
|
||||
If the connection is refused, the `SSH server` box was probably left unchecked at the tasksel screen. Plug a screen back in, log in locally and run `sudo apt install openssh-server`.
|
||||
::
|
||||
|
||||
The screen and keyboard are no longer needed. Unplug them, the machine can go live in its corner.
|
||||
|
||||
#### Log in with a key instead of a password
|
||||
|
||||
Passwords over SSH get brute-forced the moment the port is reachable from outside, and typing one on every connection gets old fast. Still on the other machine, generate a key if you don't already have one:
|
||||
|
||||
```bash [Terminal]
|
||||
ssh-keygen -t ed25519
|
||||
```
|
||||
|
||||
Press :kbd{value="Enter"} to accept the default path, and set a passphrase (it protects the key file itself, your system will remember it after the first unlock). Then copy the public half to the server. Windows has no `ssh-copy-id`, so it pushes the key over the connection instead:
|
||||
|
||||
::code-group
|
||||
```bash [macOS]
|
||||
ssh-copy-id [email protected]
|
||||
```
|
||||
|
||||
```bash [Windows]
|
||||
type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh [email protected] "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
|
||||
```
|
||||
|
||||
```bash [Linux]
|
||||
ssh-copy-id [email protected]
|
||||
```
|
||||
::
|
||||
|
||||
It asks for your password one final time. Reconnect to check that it no longer does:
|
||||
|
||||
```bash [Terminal]
|
||||
ssh [email protected]
|
||||
```
|
||||
|
||||
#### Close the door behind you
|
||||
|
||||
Once the key works, turn off password logins and direct root access. On the server:
|
||||
|
||||
```bash [Terminal]
|
||||
sudo nano /etc/ssh/sshd_config.d/hardening.conf
|
||||
```
|
||||
|
||||
```properties [hardening.conf]
|
||||
PasswordAuthentication no
|
||||
PermitRootLogin no
|
||||
KbdInteractiveAuthentication no
|
||||
```
|
||||
|
||||
A file in `sshd_config.d/` is read on top of the main config, so your changes survive a package upgrade rewriting `/etc/ssh/sshd_config`. Apply it:
|
||||
|
||||
```bash [Terminal]
|
||||
sudo systemctl restart ssh
|
||||
```
|
||||
|
||||
::warning
|
||||
Keep your current SSH session open while you test. Open a **second** terminal and connect again: if the key stopped working, the still-open session is your way back in to fix the config. Close it before you've checked and a typo locks you out of your own server, leaving the screen and keyboard as the only way back.
|
||||
::
|
||||
|
||||
::note
|
||||
The door is now closed for every other machine too, including the next one you'll want to connect from. To let a new one in, set `PasswordAuthentication yes` back in `hardening.conf`, restart SSH, run the two key steps above from that machine, then set it to `no` again and restart SSH one last time.
|
||||
::
|
||||
|
||||
#### Done !
|
||||
::
|
||||
|
||||
### Wake the server up remotely
|
||||
|
||||
A machine that runs 24/7 for two hours of actual use burns power, spins fans and wears drives for nothing. Wake on LAN lets you shut it down properly when you're done and bring it back in a few seconds without walking to it: the network card stays powered in standby, listening for one specific broadcast (the *magic packet*) carrying the server's MAC address, and switches the machine on when it sees it. Handy for a backup target you only need at night, or a media server nobody watches during the day.
|
||||
|
||||
Two conditions before you start: the machine has to be wired to ethernet, WiFi cards almost never support this, and the packet has to be sent from the same local network, since a broadcast doesn't cross a router. The BIOS side was covered in [BIOS setup](#bios-setup), here is the Debian side.
|
||||
|
||||
::steps{level="4"}
|
||||
#### Find the interface and its MAC address
|
||||
|
||||
```bash [Terminal]
|
||||
ip -br link
|
||||
```
|
||||
|
||||
You get something like `enp1s0 UP aa:bb:cc:dd:ee:ff`. Keep both: the interface name for the commands below, the MAC address for the machine that will send the packet.
|
||||
|
||||
#### Check the card supports it
|
||||
|
||||
```bash [Terminal]
|
||||
sudo apt install ethtool
|
||||
sudo ethtool enp1s0 | grep -i wake
|
||||
```
|
||||
|
||||
The answer looks like `Supports Wake-on: pumbg` then `Wake-on: d`. The letter that matters is **g**, for magic packet. If the *Supports* line doesn't have it, the card can't do it and there's nothing to configure. `Wake-on: d` simply means disabled, which the next step fixes.
|
||||
|
||||
#### Turn it on
|
||||
|
||||
```bash [Terminal]
|
||||
sudo ethtool -s enp1s0 wol g
|
||||
```
|
||||
|
||||
Run the check again, `Wake-on` should now be `g`. This setting is reset at every boot, so it needs to be reapplied automatically.
|
||||
|
||||
#### Make it survive reboots
|
||||
|
||||
```bash [Terminal]
|
||||
sudo nano /etc/systemd/system/wol.service
|
||||
```
|
||||
|
||||
```ini [wol.service]
|
||||
[Unit]
|
||||
Description=Enable Wake on LAN
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/sbin/ethtool -s enp1s0 wol g
|
||||
|
||||
[Install]
|
||||
WantedBy=basic.target
|
||||
```
|
||||
|
||||
```bash [Terminal]
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now wol.service
|
||||
```
|
||||
|
||||
#### Wake it up
|
||||
|
||||
Shut the server down with `sudo poweroff`, then send the magic packet from another machine on the network. On macOS and Linux, the `wakeonlan` package does it in one command:
|
||||
|
||||
```bash [Terminal]
|
||||
wakeonlan aa:bb:cc:dd:ee:ff
|
||||
```
|
||||
|
||||
Windows has no built-in sender, so the simplest route there is a phone app: any of the free *Wake on LAN* apps takes the MAC address and works the same way. The server should start within a couple of seconds.
|
||||
|
||||
#### Done !
|
||||
::
|
||||
|
||||
::note
|
||||
Waking it from outside your home is another story, and going through a VPN doesn't help if that VPN runs on the server itself: the tunnel is down for as long as the machine is off. The way around it is to forward a UDP port on the router (7 or 9, the usual Wake on LAN ports) to the server, then send the packet to your public address from an app that handles it, [WolOn](https://wolon.app/) for instance. The [NAT rule](/general/networking/nat) is a plain one, `UDP 9` from the outside to `192.168.1.42:9` on the inside. The router still has to point that IP at the right MAC address while the machine is off, which is why some of them expose a static ARP entry, or a Wake on LAN button of their own that saves you the port forward entirely. Worth checking your router first.
|
||||
::
|
||||
|
||||
### Keep it up to date
|
||||
|
||||
Debian doesn't update itself. Every month or so, or whenever you think about it, four commands over SSH:
|
||||
|
||||
::steps{level="4"}
|
||||
#### Refresh the package lists
|
||||
|
||||
```bash [Terminal]
|
||||
sudo apt update
|
||||
```
|
||||
|
||||
Nothing is installed at this point, `apt` only asks the mirrors what's available and tells you how many packages are behind.
|
||||
|
||||
#### Apply the updates
|
||||
|
||||
```bash [Terminal]
|
||||
sudo apt full-upgrade
|
||||
```
|
||||
|
||||
`full-upgrade` is preferred over plain `upgrade` because it accepts removing a package when that's what it takes to move another one forward, which does happen on a server that lives for years. Read the summary before answering yes, it lists exactly what gets removed.
|
||||
|
||||
#### Clean up behind them
|
||||
|
||||
```bash [Terminal]
|
||||
sudo apt autoremove --purge
|
||||
```
|
||||
|
||||
Every kernel update leaves the previous one installed, and `/boot` is a small partition that eventually fills up and breaks the next upgrade. Do this every single time, not once in a while. `--purge` also drops the config files of the packages being removed.
|
||||
|
||||
#### Reboot if the kernel moved
|
||||
|
||||
```bash [Terminal]
|
||||
sudo reboot
|
||||
```
|
||||
|
||||
A kernel or libc update only takes effect after a restart. Everything else applies immediately, so this is only needed when the upgrade touched one of those, and it's worth planning for a moment when nothing depends on the machine.
|
||||
|
||||
#### Done !
|
||||
::
|
||||
|
||||
::tip
|
||||
If you don't need to watch what's going on, the first three steps fit on one line, `&&` stopping the chain as soon as one of them fails:
|
||||
|
||||
```bash [Terminal]
|
||||
sudo apt update && sudo apt full-upgrade -y && sudo apt autoremove --purge -y
|
||||
```
|
||||
|
||||
`-y` answers yes to every question, including the day an upgrade proposes to remove something you would rather have kept, so keep it for routine rounds. Append `&& sudo reboot` to get the restart out of the way too.
|
||||
::
|
||||
|
||||
For security patches without having to think about it, `sudo apt install unattended-upgrades` then `sudo dpkg-reconfigure -plow unattended-upgrades` applies them on its own every night. Note that all of this only covers the system: your containers are updated separately, from Dockge.
|
||||
|
||||
### Going further
|
||||
|
||||
- [Everything About Remote Console Access (SSH)](https://www.digitalocean.com/community/tutorials/ssh-essentials-working-with-ssh-servers-clients-and-keys)
|
||||
- Optional - [UPS Client in Case of Power Outage](https://www.sindastra.de/p/2078/how-to-connect-linux-server-to-synology-ups-server) / [also here](https://www.reddit.com/r/synology/comments/gtkjam/use_synology_nas_as_ups_server_to_safely_power/)
|
||||
|
||||
## Must-Have CLI Apps
|
||||
Some essential apps you’ll likely need at some point, so might as well install them early:
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 104 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 716 KiB |
Reference in New Issue
Block a user