Pimp My Laptop: Minimal Linux on a ThinkPad X260
I wanted a laptop that felt like mine again.

Not a laptop buried under vendor defaults. Not a machine full of apps I never opened. Not a desktop that made every simple action feel like it had to pass through three layers of settings menus first. I wanted something plain, fast, keyboard-driven, and easy to understand.
So I took a Lenovo ThinkPad X260 and decided to pimp it out the way I wanted: Debian Minimal, suckless tools, a black/red ThinkPad-inspired theme, a custom wallpaper, a tiny status bar, and dotfiles I could actually read.

This is not meant to be a perfect beginner installation guide. It is a build log: the decisions, commands, screenshots, problems, and cleanup work involved in turning a used ThinkPad into a small Linux workstation with personality.
If you want to copy pieces of the setup instead of just reading about it, the dotfiles live here: gitlab.com/sean_wong/pimp-my-laptop. The repo includes the dwm, st, and dmenu source trees, the shell scripts that drive the bar, the home-directory dotfiles, and the visual assets that tie the theme together.
Quick summary I used Debian Minimal as the base, built
dwm,st, anddmenufrom source, themed the desktop around a black/red Event Horizon palette, added a shell-script status bar, then organized the finished setup into a dotfiles repo that can be rebuilt later.
The project arc was simple:
used ThinkPad
→ Debian Minimal
→ first TTY login
→ X11 test
→ vanilla dwm
→ Event Horizon theme
→ practical daily apps
→ organized dotfiles repo
The starting point
The laptop was a Lenovo ThinkPad X260: small, matte, practical, and exactly the kind of machine that makes you want to install Linux on it. It already had the ThinkPad things I care about: a good keyboard, a TrackPoint, a compact body, and a design that feels more like a tool than a fashion object.

Before changing anything, I took photos. That felt unnecessary at first, but I’m glad I did it. A project like this is more satisfying when you can see the before and after side by side.


The old desktop was fine. That was the problem. It worked, but it didn’t feel intentional. I wanted the machine to boot into a setup where every visible thing had been chosen: the window manager, the font, the colors, the wallpaper, the bar, the terminal, and even the prompt.

The keyboard also made the direction obvious. A tiling window manager only really makes sense if the keyboard feels good enough to use all day. On the X260, it does.

The vision: practical minimalism
The goal wasn’t to make the most minimal Linux install possible. I wanted a desktop that still felt polished.
The final theme name became:
event-horizon
The idea was:
ThinkPad black/red hardware language
+ suckless minimalism
+ black hole / event horizon wallpaper
+ dark terminal-focused workflow
The design direction was simple:
- black and near-black backgrounds:
#050507and#101014 - ThinkPad red as the accent color:
#E2232A - muted gray text for inactive UI:
#858892 - bright text where readability matters:
#D7D7D9and#FFFFFF - orange and dark red as supporting colors:
#FF6A1A,#8B0000, and#B00020 - JetBrains Mono everywhere possible
- no giant panels, widgets, or visual noise
The rough visual ratio was:
85–90% black
5–10% gray detail
1–5% red/orange accent
The wallpaper solved the visual direction. A literal ThinkPad wallpaper felt too obvious. The black hole image gave the setup a darker, more distinctive identity while still pairing naturally with the ThinkPad’s black chassis and red TrackPoint.
The stack ended up looking like this:
- Debian Minimal as the base system
dwmfor window managementstfor the terminaldmenufor launching programsslockfor lockingpicomfor subtle compositingfehfor wallpaperrangerfor file browsing- Vim for editing
- Firefox ESR as the practical browser
- surf as the minimal browser
- Bash scripts for the status bar
That list is not pure minimalism. It’s practical minimalism. I still wanted image previews, a real browser, nice fonts, screenshots, media keys, and enough quality-of-life polish to enjoy the machine.
Installing Debian Minimal
The install started the normal way: download the ISO, make a bootable USB, boot into the installer, and get a base Linux system onto the machine.

I went with Debian because I wanted the base system to be calm. Arch would have been fun. Void would have been interesting. Alpine would have been ultra-minimal. Debian felt right because the whole project was already going to involve building and configuring a lot of desktop pieces by hand.

The important part at this stage was not ricing anything yet. It was getting the basics right: a booting system, networking, a user account, sudo, Git, Xorg, fonts, and the development libraries needed to build the suckless tools.
The install checklist was:
- create installation media
- enter BIOS and confirm USB boot
- choose UEFI or legacy settings
- configure Secure Boot if needed
- partition and format the disk
- install the base system
- configure hostname, locale, timezone, and user account
- install the bootloader
- reboot successfully
Once the machine could boot into a console, the laptop started to feel like a blank canvas.
First login, SSH, and the first signs of life
After the install, I logged into the TTY and started building the environment from the bottom up. This is one of my favorite parts of a Linux setup because nothing is hidden yet. You install exactly what you need, then watch the desktop appear piece by piece.
The first post-install tasks were basic:
sudo apt update
sudo apt install sudo git
Useful system checks at this stage:
whoami
hostnamectl
uname -a
ip a
fastfetch
One practical improvement was enabling SSH early. After that, I could keep the ThinkPad as the target machine while using my main computer to paste commands, edit notes, and transfer files instead of typing everything manually on the laptop.
sudo apt update
sudo apt install openssh-server
sudo systemctl enable --now ssh
systemctl status ssh
hostname -I
From the main machine:
ssh yourusername@THINKPAD_IP
Optional key setup:
ssh-copy-id yourusername@THINKPAD_IP
Or from macOS:
cat ~/.ssh/id_ed25519.pub | ssh yourusername@THINKPAD_IP "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
That small step made the rest of the project much smoother.
From TTY to X11
Before worrying about dwm, wallpaper, compositing, or themes, I wanted to know that the graphical stack worked.
The X11 test package set was:
sudo apt update
sudo apt install xorg xinit
Fonts:
sudo apt install fonts-jetbrains-mono fonts-noto fonts-noto-color-emoji fonts-font-awesome fonts-hack
The temporary .xinitrc was intentionally boring:
echo "exec xterm" > ~/.xinitrc
startx
That first X session was ugly, but that was the point. It proved the graphical stack worked before I started blaming dwm, fonts, wallpaper, or my own config.
Building the suckless desktop
Once X worked, I could start bringing in the suckless tools. The repo vendors dwm, st, and dmenu directly, which means the configuration is visible in the source tree instead of being scattered across hidden application state.
Build dependencies:
sudo apt update
sudo apt install build-essential git libx11-dev libxft-dev libxinerama-dev libfontconfig1-dev
The initial source flow was:
mkdir -p ~/src
cd ~/src
git clone https://git.suckless.org/dwm
git clone https://git.suckless.org/st
git clone https://git.suckless.org/dmenu
The basic build flow is straightforward:
cd suckless/dwm
make
sudo make install
cd ../st
make
sudo make install
cd ../dmenu
make
sudo make install
For a first launch, .xinitrc can be as small as:
echo "exec dwm" > ~/.xinitrc
startx

The first dwm launch was the first time the laptop started to feel like the project I had imagined. It was still plain, but the core workflow was there: dwm for window management, st for the terminal, and dmenu for launching programs.
Default controls worth learning first:
| Action | Keybinding |
|---|---|
| Open terminal | Alt + Shift + Enter |
| Open dmenu | Alt + p |
| Close window | Alt + Shift + c |
| Quit dwm | Alt + Shift + q |
| Switch tags | Alt + 1, Alt + 2, etc. |
Turning vanilla dwm into a laptop session
After the first launch, .xinitrc became the launch point for the whole desktop.
[ -f ~/.Xresources ] && xrdb -merge ~/.Xresources
feh --bg-fill ~/Pictures/wallpapers/wallpaper.png &
picom &
xsetroot -cursor_name left_ptr
[ -x ~/.local/scripts/dwm-status.sh ] && ~/.local/scripts/dwm-status.sh &
command -v xss-lock >/dev/null 2>&1 && xss-lock -- slock &
xset s 300 5
xset dpms 600 900 1200
exec dwm
That file is small, but it tells the whole story: load colors, set wallpaper, start the compositor, set the cursor, launch the status bar, enable idle locking, set screen blanking and DPMS timings, then hand control to dwm.
The dependency list in the repo reflects the practical balance. It includes the obvious build and X11 pieces, like build-essential, xorg, xinit, libx11-dev, libxft-dev, and libxinerama-dev. It also includes desktop comfort tools: picom, feh, slock, xss-lock, btop, cmus, firefox-esr, surf, ranger, vim, scrot, fastfetch, alsa-utils, xbacklight or brightnessctl, bat, chafa, glow, and highlight.
sudo apt update
sudo apt install feh picom scrot xclip x11-xserver-utils dbus-x11 fastfetch
sudo apt install brightnessctl alsa-utils pavucontrol
The dwm config
The dwm config became the center of the setup. Tags use Chinese numerals. The selected tag uses ThinkPad red. Firefox ESR and surf go to tag nine. Media keys handle volume, brightness, screenshots, and cmus playback.
一 二 三 四 五 六 七 八 九
The project notes originally kept numbered tags to avoid font rendering problems:
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
The final direction uses the same principle but with more personality.
Core visual settings:
static const char *fonts[] = { "JetBrains Mono:size=10" };
static const char dmenufont[] = "JetBrains Mono:size=10";
static const unsigned int borderpx = 2;
static const unsigned int snap = 32;
static const int showbar = 1;
static const int topbar = 1;
Color settings:
static const char col_gray1[] = "#050507"; // background
static const char col_gray2[] = "#2A2B31"; // inactive border
static const char col_gray3[] = "#858892"; // muted text
static const char col_gray4[] = "#D7D7D9"; // foreground
static const char col_cyan[] = "#E2232A"; // active red
static const char *colors[][3] = {
/* fg bg border */
[SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
[SchemeSel] = { col_gray4, col_gray1, col_cyan },
};
I kept the bindings close to default where it made sense. Alt+p opens dmenu. Alt+Shift+Enter opens st. Alt+b toggles the bar. Alt+j and Alt+k move through windows. Alt+h and Alt+l resize the master area.
Then I added laptop-friendly extras:
Alt+wopens Firefox ESRAlt+Shift+wopens surfPrintsaves a screenshot- volume keys call
amixer - brightness keys use
brightnessctlorxbacklight - playback keys control
cmus
That made the machine feel less like a demo config and more like a laptop. A minimal desktop still needs laptop buttons to work.
The biggest design choice here was restraint. I could have added more patches, more layouts, more icons, and more status modules. Instead, I wanted the config to stay readable enough that future me could open it six months later and still understand what I had done.
The visual details are all in suckless/dwm/config.h. The border is 2px, the snap distance is 32px, the bar lives at the top, and the main layout keeps one master window with mfact set to 0.55. The layouts are the standard trio: tiled []=, floating ><>, and monocle [M].
The window rules are simple too. Firefox, Firefox ESR, and surf are sent to tag nine. I like that because browsers can become their own universe. Keeping them on the last tag makes the rest of the workspace feel quieter.

dmenu: quiet until selected
dmenu inherits the same font and palette from the dwm command: JetBrains Mono at size 10, #050507 background, muted gray normal text, ThinkPad red selection, and bright selected text. That makes the launcher feel like part of the desktop instead of a separate app.
The notes version looked like this:
static const char *fonts[] = {
"JetBrains Mono:size=10"
};
static const char *colors[SchemeLast][2] = {
/* fg bg */
[SchemeNorm] = { "#858892", "#050507" },
[SchemeSel] = { "#050507", "#E2232A" },
[SchemeOut] = { "#000000", "#00ffff" },
};
The point was to keep dmenu visually quiet until selection. Normal state is muted gray on black. Selection gets the TrackPoint red accent.
Making the terminal feel right
The terminal is where most of the personality lives. I patched and configured st with a dark background, transparency, JetBrains Mono, and the same black/red/gray palette used by dwm.
The terminal config uses JetBrains Mono:pixelsize=14:antialias=true:autohint=true, a 2px border, st-256color, and alpha = 0.88 for transparency. The default size stays classic at 80 columns by 24 rows. The cursor is a block, the bell volume is disabled, and the alt screen is allowed so full-screen terminal apps behave normally.
Font:
static char *font = "JetBrains Mono:pixelsize=14:antialias=true:autohint=true";
Palette:
static const char *colorname[] = {
"#050507", /* black */
"#E2232A", /* red */
"#858892", /* green */
"#FF6A1A", /* yellow */
"#2A2B31", /* blue */
"#8B0000", /* magenta */
"#D7D7D9", /* cyan */
"#D7D7D9", /* white */
"#101014", /* bright black */
"#FF3B3F", /* bright red */
"#A0A4AD", /* bright green */
"#FF8A3D", /* bright yellow */
"#3A3B42", /* bright blue */
"#B00020", /* bright magenta */
"#FFFFFF", /* bright cyan */
"#FFFFFF", /* bright white */
[255] = 0,
"#D7D7D9", /* foreground */
"#050507", /* background */
"#E2232A", /* cursor */
};
Defaults:
unsigned int defaultfg = 256;
unsigned int defaultbg = 257;
static unsigned int defaultcs = 258;
Transparency was the first patch that caused friction. An older alpha patch partially applied but failed one hunk, so I reset the st source and used a newer alpha patch instead:
st-alpha-20240814-a0274bc.diff
https://st.suckless.org/patches/alpha/st-alpha-20240814-a0274bc.diff
Opacity tests:
st -A 0.92
st -A 0.88
st -A 0.84
st -A 0.80
My notes from testing:
0.92 = very subtle
0.88 = best balance
0.84 = more stylish
0.80 = probably too transparent
The shell prompt got the same treatment. The username and host use the red accent, the current directory stays bright, and the prompt marker matches the theme.
sing@box:~/src $
I hit one funny bug here. The prompt briefly showed a broken-looking string because I escaped the dollar sign incorrectly. Instead of a clean $, Bash expanded part of the prompt like a process ID and left the reset variable visible. Tiny mistake, ugly result.
That’s the nice thing about keeping dotfiles simple, though. The bug was in one line of .bashrc. No theme engine. No generated config. Just a bad escape sequence that needed to be fixed.
The shell also adds a few daily aliases:
alias fd='fdfind'
alias v='nvim'
alias ll='ls -lah'
alias gs='git status'
alias c='clear'
alias ss='scrot ~/Pictures/screenshots/%Y-%m-%d-%H%M%S.png'
alias ff='fastfetch'
alias lock='slock'
Wallpaper, picom, and Xresources
The chosen wallpaper has:
mostly black background
small/medium black hole off-center
red/orange accretion disk
lots of negative space
subtle sci-fi mood
Stored at:
~/Pictures/wallpapers/wallpaper.png
.Xresources sets the shared X11 palette, Xft.dpi: 96, antialiasing, light hinting, RGB subpixel rendering, JetBrains Mono at size 10, the red cursor color, and matching XTerm fallbacks. Since .xinitrc loads it before everything else, the theme becomes part of the session startup rather than an afterthought.
picom is intentionally subtle. The repo uses the xrender backend, vsync, soft shadows with radius 8, fade steps of 0.03, inactive opacity at 0.96, active opacity at 1.0, and no rounded corners. It adds polish without making the desktop feel like a different environment.
backend = "xrender";
vsync = true;
shadow = true;
shadow-radius = 8;
shadow-offset-x = -6;
shadow-offset-y = -6;
shadow-opacity = 0.22;
fading = true;
fade-in-step = 0.03;
fade-out-step = 0.03;
inactive-opacity = 0.96;
active-opacity = 1.0;
frame-opacity = 1.0;
corner-radius = 0;
detect-client-opacity = true;
detect-transient = true;
detect-client-leader = true;
The status bar: just shell scripts
I didn’t want a heavy bar. dwm already displays the root window name, so the status bar can be a shell loop that periodically runs small scripts and passes the result to xsetroot.
The simple early version looked like this:
#!/bin/sh
while true; do
bat="$(cat /sys/class/power_supply/BAT*/capacity 2>/dev/null | head -n 1)"
time="$(date '+%a %b %d %H:%M')"
if [ -n "$bat" ]; then
xsetroot -name " BAT ${bat}% | ${time} "
else
xsetroot -name " ${time} "
fi
sleep 30
done
The later script refreshes every five seconds and looks for its helper scripts in $HOME/.local/scripts unless DWM_STATUS_SCRIPT_DIR is set. That makes the bar easy to test from another folder while keeping the normal installed location predictable.
The aggregator looks like this:
append_part setcmus.sh
append_part setbat.sh
append_part setbright.sh
append_part setvol.sh
append_part setclock.sh
xsetroot -name "$status"
Each script does one job. One checks battery. One checks brightness. One checks volume. One checks cmus. One prints the clock. The output is joined with separators and displayed in the bar.
The battery script reads /sys/class/power_supply/BAT* and prints a plug icon while charging. The brightness script tries xbacklight, then brightnessctl, then falls back to /sys/class/backlight. The volume script reads amixer sget Master and switches icons when muted. The cmus script checks pgrep cmus before calling cmus-remote -Q.
97% | 70% | 42% | Sunday 2026-05-24 | 20:30
This also created a useful reminder: scripts need executable bits. If the status bar doesn’t start, check that ~/.local/scripts/dwm-status.sh and the set*.sh files are executable.
chmod +x ~/.local/bin/dwm-status
chmod +x ~/.local/bin/shot
chmod +x ~/.local/scripts/*.sh
In this repo, the intended installed path is ~/.local/scripts. If I later add an install script, one of its jobs should be copying the scripts there and running chmod +x on every .sh file.

Screenshots and small utilities
The screenshot helper is intentionally boring:
#!/bin/sh
mkdir -p "$HOME/Pictures/screenshots"
scrot "$HOME/Pictures/screenshots/%Y-%m-%d-%H%M%S.png"
That gets bound to a key and keeps screenshots in one expected folder.
This sounds minor, but it changed how I documented the project. Once screenshots were easy, I captured more of the middle stages instead of only the final desktop. That gave the article a better before-and-after arc.
Essential applications
After the core desktop worked, the project became less about bootstrapping and more about turning the machine into something I could use.
Practical app stack:
sudo apt update
sudo apt install firefox-esr neovim tmux ranger zathura mpv htop unzip curl wget ripgrep fd-find
Ranger preview helpers:
sudo apt install highlight atool w3m poppler-utils mediainfo
Browser decision:
surf = minimal/suckless browser
Firefox ESR = practical daily browser
I installed surf like this:
sudo apt install surf
surf https://suckless.org
Optional tabbed setup:
sudo apt install tabbed
Or:
cd ~/src
git clone https://git.suckless.org/tabbed
cd tabbed
sudo make clean install
Launch:
tabbed surf -e
Ranger, Vim, Firefox, and surf polish
Ranger gets a bigger setup than it might look at first. Columns use a 1,3,4 ratio, previews are on, borders are drawn on both sides, sorting is natural and case-insensitive, VCS awareness is enabled, tabs show directory names, and the default line mode uses devicons.
The ranger mappings are tuned for quick movement: gh goes home, gd goes to downloads, gc goes to ~/.config, gs goes to ~/src, gp goes to pictures, and gu goes to /media. There are also quick commands for toggling hidden files, reloading the current directory, renaming, making directories, touching files, editing, opening a shell, and making files executable.
The preview script is where ranger gets fun. Archives are listed with tar or unzip. Images render through chafa when available. PDFs are previewed with pdftotext. Markdown can use glow -s dark. Code and text fall back to bat with line numbers or highlight if bat is missing.
Firefox ESR got a small profile folder with user.js defaults and a matching userChrome.css. Surf got a stylesheet and script for dark page hints, red focus rings, and matching selection color.
The Firefox defaults turn on dark content and toolbar themes, hide the about:config warning, quiet the new tab page, disable sponsored content, enable compact density, and turn on toolkit.legacyUserProfileCustomizations.stylesheets so userChrome.css works. They also enable HTTPS-only mode, Do Not Track, Global Privacy Control, tracking protection, and a few low-telemetry defaults.
The Firefox chrome CSS uses the same variables as the desktop: --pml-bg, --pml-panel, --pml-muted, --pml-fg, and --pml-red. Selected tabs go red, inactive labels go muted gray, and the URL bar gets a red border when focused.
Surf stays much simpler. Its CSS sets color-scheme: dark, a #050507 page background, optimized text rendering, red selection, inherited form fonts, and red focus outlines. The script nudges pages toward a dark color scheme and matching scrollbar colors.
Vim is configured as the editor side of the same setup. It uses vim-plug with NERDTree, vim-airline, airline themes, gruvbox, vim-commentary, and vim-surround. The leader key is comma. It enables relative line numbers, persistent undo, split navigation with Ctrl-h/j/k/l, a hard dark gruvbox theme, transparent backgrounds, a red cursor line number, and a color column at 81.
The Vim mappings are practical: leader-space clears search, leader-ev opens the vimrc, leader-sv reloads it, leader-n toggles NERDTree, leader-f finds the current file, leader-sc toggles spellcheck, leader-w writes, leader-q quits, and leader-x deletes the buffer. It even has small LaTeX helpers for pdflatex and opening the result in zathura.
None of these pieces are dramatic by themselves. Together, they make the desktop feel like one system instead of a pile of defaults.

The wallpaper tied it together. With the black/red palette, transparent terminal, and simple bar, the laptop finally had the feeling I wanted: minimal, but not empty.

Organizing the dotfiles
Once the machine looked right, I cleaned up the repository so the setup could be rebuilt later. The blog post shows the process, but the repo is the useful artifact: it is where the actual config lives.
.
├── assets/
│ ├── logo.png
│ └── wallpaper/
├── blog/
│ └── pimp-my-laptop/
│ ├── index.md
│ ├── images/
│ └── notes/
├── home/
│ ├── .Xresources
│ ├── .bashrc
│ ├── .config/
│ ├── .mozilla/
│ ├── .surf/
│ └── .xinitrc
├── scripts/
└── suckless/
├── dmenu/
├── dwm/
└── st/
The home/ directory mirrors files that belong in $HOME. The scripts/ folder contains the status bar helpers and utility scripts. The suckless/ folder stores the patched source for dwm, st, and dmenu. The blog/ folder keeps the photos and writing separate from the actual dotfiles.
Want the dotfiles? Browse or clone the setup at gitlab.com/sean_wong/pimp-my-laptop. Start with
home/for the user config,scripts/for the status bar pieces, andsuckless/if you want the patcheddwm,st, anddmenusources.
What the repo contains
The GitLab repo is meant to be more than a screenshot dump. It holds the pieces needed to understand and rebuild the setup:
home/contains the dotfiles that mirror my home directoryscripts/contains the status bar helpers and utility scriptssuckless/contains the configured source trees fordwm,st, anddmenuassets/contains visual assets like the wallpaper and logoblog/contains the writing notes, screenshots, and post material
That structure keeps the laptop setup separate from the story about the setup. It also makes the repo easier to clone without guessing which files belong where. If I rebuild the X260, move the setup to another laptop, or just steal one piece of the status bar, this is the place I would start.
Process recap
| Stage | What changed |
|---|---|
| Before laptop | Original ThinkPad X260 and Windows/default state |
| Installer | Debian Minimal installation |
| First graphical session | X11 test, then first dwm launch |
| Theme pass | Event Horizon palette, wallpaper, dwm/st/dmenu colors |
| Practical desktop | status bar, ranger, Vim, Firefox, surf, media keys |
| Final desktop | finished dwm setup |
What I’d still add
I’d add an install script. It doesn’t need to be clever. It should copy dotfiles, copy scripts, create expected folders, and set executable bits.
I’d also remove local junk before committing: .DS_Store, Vim runtime state, and installed plugin folders. Dotfiles repos are personal, but they still deserve a clean first commit.
If I keep developing this setup, the next useful upgrades are boring ones: battery tuning, a cleaner backup routine, and a short troubleshooting page.
What I learned
A few lessons came out of the project.
Debian Minimal was a good base for a stable lightweight setup. It was boring in the best way. I could spend my attention on the desktop rather than on constant system maintenance.
Building suckless tools from source was simple, but patch compatibility matters.
Restrained visual changes made the desktop feel more usable. The setup works because most of it stays black, quiet, and readable. The red accent is effective because it is not everywhere.
The wallpaper and color palette carried more of the aesthetic than heavy customization. I didn’t need a giant widget system or a complicated bar to make the machine feel finished.
Capturing screenshots at each stage made the transformation easier to tell as a story. The before photos, installer images, first terminal, middle theme pass, and final desktop give the project a real arc.
The annoying parts were small shell details. A broken prompt escape and missing executable bits caused more visible weirdness than the big pieces. The lesson is simple: keep configs readable, then document the assumptions.
Was it worth it?
Yes. Not because the laptop is now objectively better than every modern machine. It isn’t. It was worth it because the machine now feels intentional.
The X260 boots into a desktop I understand. The config files are small. The colors match. The terminal feels good. The browser has its place. The file manager has previews. The media keys work. The bar says only what I want it to say.

That’s the whole appeal of this kind of project. You start with a used laptop and end with a machine that feels built around your hands.
It’s not just Linux on a ThinkPad. It’s my ThinkPad now.
FAQ
Why use an older ThinkPad for this?
Older ThinkPads are great for this kind of project because they’re sturdy, keyboard-friendly, easy to find used, and well supported by Linux. The X260 is small enough to carry around but still feels like a real laptop.
Why use Debian Minimal instead of Arch, Void, or Alpine?
Arch would have been the most educational and customizable. Void would have been interesting. Alpine would have been ultra-minimal. For this project, I wanted a base that was minimal without becoming a constant maintenance project. Debian Minimal felt stable, boring in the best way, and flexible enough to build a suckless desktop from source.
Why use dwm instead of GNOME, KDE, or XFCE?
I wanted the desktop to be configured through code and keybindings instead of settings panels. dwm keeps the whole window manager small enough to understand. That makes it a good fit for a personal dotfiles project.
Is this setup practical for daily use?
Yes, as long as you enjoy a keyboard-driven workflow. It has a real browser, file manager, editor, media keys, screenshots, lock screen, wallpaper, and status bar. It’s minimal, but it isn’t bare.
Do you have to compile the suckless tools?
For this setup, yes. The repo includes patched source for dwm, st, and dmenu, so building them is part of the process. That’s also what makes the setup easy to customize later.