All of Linux configuration lives in plain text files. Your shell aliases, your editor keybindings, your window manager layout, your SSH shortcuts — all of it is a file in your home directory. This is how that works.
Section 01
A dotfile is any file whose name starts with a dot. That's the entire definition.
On Linux, files beginning with . are hidden by default — ls
skips them. ls -a shows them. The convention comes from Unix: the
developers wanted to hide unimportant files like . (current dir) and
.. (parent dir), and the behavior accidentally became the mechanism
for hiding all user configuration files.
Dotfiles are plain text. No binary format, no registry, no app-specific database. You open them in any editor, read them, change them, and the change takes effect immediately (or on next login, depending on the file). This is one of the things that makes Linux configuration so powerful — and so portable. Your entire working environment is just text files.
The old convention was to put config directly in ~/ as
~/.appname or ~/.appnamerc. This works, but
after twenty apps it turns your home directory into a mess.
The XDG Base Directory Specification standardized a cleaner
approach: config goes in ~/.config/<appname>/, cached data
in ~/.cache/, and runtime files in ~/.local/share/.
Most modern apps follow this. Older apps (bash, vim, git, ssh) predate it
and still use ~/. directly.
You'll see both conventions in the wild. The location doesn't change what
the file does — only where it lives. The environment variable
$XDG_CONFIG_HOME defaults to ~/.config; some
apps let you override it.
Section 02
Three files, and they're not the same. Bash draws a distinction between a
login shell (first session when you log into a machine) and an
interactive shell (every terminal you open after that).
Login shells read ~/.bash_profile. Interactive shells read
~/.bashrc. In practice, most people just put everything
in ~/.bashrc and source it from ~/.bash_profile.
When you first log in — at a TTY, over SSH, or via a display manager —
Bash is running as a login shell. It reads
/etc/profile (system-wide), then ~/.bash_profile
or ~/.profile. This is where you set things that only need to
happen once per session: PATH additions, environment variables, auto-starting X.
Every terminal you open afterward is an interactive shell.
It reads ~/.bashrc. This is where your aliases, functions,
and prompt live — things that should be available in every terminal window.
The standard pattern: put everything useful in ~/.bashrc,
and have ~/.bash_profile source it so login shells also
get your aliases and prompt.
Start here. Every item earns its place.
Zsh users: ~/.zshrc serves the same role as
~/.bashrc. The syntax for aliases and exports is identical.
The prompt variable is PROMPT (or use a prompt theme like
starship). There is no ~/.zsh_profile
equivalent — zsh uses ~/.zprofile and ~/.zshenv.
If you're running a WM setup without a display manager (the approach in
Appendix B),
this snippet in ~/.bash_profile automatically launches X when
you log in on TTY1 — without running a display manager daemon in the background.
Log in on TTY2 (Ctrl+Alt+F2) and you still get a plain terminal session. Only TTY1 starts X automatically.
Section 03
Git won't let you make a commit without a name and email. That's the minimum.
But ~/.gitconfig is also where you set default behavior and create
shortcuts that save real time every day.
Test your aliases immediately: git lg shows a
compact branching graph of your whole history. git st gives a
short status. git last is useful right after a commit to confirm
what went in. Once you get used to these you'll wonder how you lived without them.
Section 04
Most people use SSH like this: ssh user@192.168.1.100 -i ~/.ssh/some_key -p 2222.
Every. Single. Time. The SSH config file eliminates all of that. You define a named
host alias once, and from then on it's just ssh myserver.
Each Host block creates a named shortcut. The Host
line is the alias you'll type. Everything indented beneath it is the actual
connection parameters.
Each host block is independent. Different servers can use different usernames, ports, and SSH keys — all resolved automatically by the alias you type.
A jump host (also called a bastion) is a server that sits on the
public internet and tunnels you into a private network behind it. You SSH to the
jump host first, then from there to the internal machine. Without the config file,
this is two separate SSH commands chained together. With ProxyJump,
it's one command — SSH handles the chain automatically.
A Host * block at the end of the file sets defaults that
apply to every connection unless overridden by a specific host block above it.
SSH reads top to bottom and uses the first matching value it finds —
so specific blocks win over the wildcard.
ServerAliveInterval prevents the "broken pipe" error you get when a terminal sits idle and the connection silently drops. Without it, you come back to a session after 20 minutes and nothing you type responds — the connection died while you weren't looking. Setting this keeps the connection alive with periodic pings.
| Directive | What it does | Example value |
|---|---|---|
| HostName | The real address (IP or hostname) to connect to | 192.168.1.100 or server.example.com |
| User | Username to log in as | mike, admin, root |
| Port | SSH port number (default is 22) | 2222 |
| IdentityFile | Path to the private key for this host | ~/.ssh/id_ed25519_work |
| ProxyJump | Route through another host alias first | bastion |
| LocalForward | Forward a local port to a remote address (tunnel) | 8080 localhost:8080 |
| ServerAliveInterval | Send keepalive every N seconds to prevent dropped connections | 60 |
| AddKeysToAgent | Automatically add key to ssh-agent on use | yes |
| IdentitiesOnly | Only try keys listed in the config, not all keys in the agent | yes |
SSH is strict about file permissions. If ~/.ssh/config or your
private keys are readable by anyone other than you, SSH will refuse to use them.
Set these once and forget about them.
What NOT to put in ~/.ssh/config: The config file holds
connection instructions, not credentials. Never paste private key
content into it. Never put passwords. Private keys live as separate files
— the IdentityFile directive just points to where they are.
The config file itself can be read-only to you, not secret — but treat
your private key files as secrets.
Section 05
Fluxbox stores its entire configuration in ~/.fluxbox/. No GUI
settings panel, no database, no app to open. Three files do most of the work,
and all three are plain text you can edit and reload without restarting.
exec fluxbox.
This is a shell script. Each tray applet launches in the background with
&, then exec fluxbox at the end hands control
to Fluxbox itself. When Fluxbox exits, the X session ends — because
exec replaces the shell process rather than spawning a child.
On a 4K or 8K display, UI elements are physically tiny unless you scale the
logical desktop down. xrandr --scale is how you do this under X11 — it
tells Xorg to present a smaller logical canvas that maps onto the full physical panel,
making every element appear proportionally larger.
--scale 0.5x0.5 halves the logical dimensions: a 4K (3840×2160)
panel behaves like a crisp 1920×1080 display.
Step 1 — find your output name
Step 2 — apply scaling
| Display | Physical res. | --scale flag | Logical res. | Effective DPI |
|---|---|---|---|---|
| 4K (UHD) | 3840×2160 | 0.5x0.5 | 1920×1080 | ~200% (2× pixels per point) |
| 4K (UHD) | 3840×2160 | 0.667x0.667 | 2560×1440 | ~150% (more screen space) |
| 8K (FUHD) | 7680×4320 | 0.5x0.5 | 3840×2160 | ~200% (4K-equivalent desktop) |
| 8K (FUHD) | 7680×4320 | 0.25x0.25 | 1920×1080 | ~400% (1080p desktop at 8K sharpness) |
To make this permanent, add the xrandr call to
~/.fluxbox/startup before the applets and before exec fluxbox.
Put it first — some tray applets read DPI on launch, and you want the scaling
active before they start.
Each connected output gets its own --output flag.
Chain them in a single xrandr call so the layout is
set atomically — calling it twice can cause a brief flicker.
For interactive multi-monitor layout, arandr generates
the xrandr command for you — then copy it into startup.
xrandr --scale is a coordinate-transformation hack —
X11 has no built-in DPI concept, so scaling is applied after the fact
by stretching the framebuffer. It works, but at some cost: the GPU
does the downsampling in software, and apps still think they're
rendering at 96 DPI unless you also set Xft.dpi.
Wayland compositors handle HiDPI natively. The compositor broadcasts a scale factor to every connected client. Apps that speak Wayland natively (GTK4, Qt6, modern Electron) receive the factor and render at full physical resolution — no interpolation, no framebuffer stretch. Text, icons, and vector graphics come out pixel-perfect. The compositor config is one line per output.
The catch: XWayland. X11 apps running inside a Wayland
session go through XWayland, which doesn't see the compositor's scale
factor. Those apps still need the old hints. Set them as environment
variables (in ~/.profile or your compositor's config)
alongside the Wayland scale:
Native Wayland apps (GTK4, Qt6, Firefox, Chromium in Wayland mode) pick up the scale factor automatically — no env vars needed. The XWayland workarounds only affect legacy X11 apps that haven't been ported.
Each line is: modifier key :Action or modifier key :ExecCommand program.
Mod1 is the Alt key. Mod4 is the Super key (Windows key).
The colon before the action is required — it separates the key combination from
the Fluxbox command.
Modifiers:
Mod1 = Alt |
Mod4 = Super (Windows key) |
Control = Ctrl |
Shift = Shift
Window actions:
After editing keys: right-click desktop → Fluxbox menu →
Reconfigure to reload without a full restart.
Right-click anywhere on the desktop and this is what you see. No dock, no
application grid — just a menu you define. The format is
[type] (Label) {command}. Submenus are nested with
[submenu] / [end] blocks.
Edit the menu file, then right-click → System → Reconfigure. Changes appear immediately without restarting Fluxbox. You can add any application, script, or command — if it runs in a terminal, it runs from the menu. See Appendix B for the complete Fluxbox setup guide including PipeWire, tray stack, and APT guardrails.
Section 06
Your editor config is personal. It's also one of the most frequently tweaked dotfiles — small changes add up to a meaningfully different editing experience over time.
Vim's config. Even if you only open Vim occasionally, a minimal
~/.vimrc makes it noticeably less abrasive.
Neovim follows the XDG spec. Configuration lives in
~/.config/nvim/ rather than ~/.vimrc.
The main file is either init.vim (Vimscript, compatible with
your existing .vimrc) or init.lua (Lua,
the modern approach). A plain init.vim that sources your
existing ~/.vimrc is a reasonable starting point:
Section 07
These don't need deep customization to be useful. Knowing they exist means you know where to look when something behaves unexpectedly.
startx when X launches. On a Fluxbox setup,
this contains just exec startfluxbox. On a minimal DE
setup, it might start a session manager instead.
xterm). Applied with
xrdb ~/.Xresources at startup — add that line to
~/.fluxbox/startup before exec fluxbox.xrandr --scale with
Xft.dpi here so fonts scale correctly too:
Xft.dpi: 192 for 200% (standard 96 × 2).
.bash_profile. Read by login
shells when .bash_profile doesn't exist, and by some
display managers. Good place for environment variables that
non-bash shells also need.
curl command. Commonly used to
set a custom user-agent, enable compressed responses, or silence
progress output in scripts.
less — the pager used
by man pages and git log. Usually not needed
until you have a specific annoyance to fix.
xrandr --scale scales the desktop canvas — but fonts and GTK apps
read the logical DPI, which stays at 96 unless you tell X11 otherwise.
Set Xft.dpi to get text and UI elements to match the visual scale.
The standard DPI is 96; double it to 192 for 200% scaling.
Scale values: 96 = 100% (standard) · 144 = 150% · 192 = 200% (4K) · 288 = 300% · 384 = 400% (8K at 0.25×)
These apply to bash tab completion, the Python REPL, psql,
and any other program that links against GNU Readline.
No restart needed — changes take effect in the next shell you open.
Section 08
Opinion
You don't need to set up all of these at once. Do it incrementally — when something annoys you, fix it with a dotfile. That's how everyone actually builds their config.
Start with ~/.gitconfig.
Git refuses to commit without your name and email. Set those first.
Then add a lg alias — the branching graph becomes immediately
useful and you'll use it constantly.
Then a handful of ~/.bashrc aliases.
ll for ls -lh, cp -i and
rm -i to protect yourself from accidents, and maybe a
shortcut for a directory you visit constantly. That's already a better
terminal.
Then ~/.ssh/config once you have more than one server.
The first time you have two machines and two keys, typing the full address
and -i ~/.ssh/key_name every time becomes tedious fast.
Five minutes to set up the config file, and you never type it again.