Editing files: nano, vim, and changing a server safely
Edit config files over SSH with nano and vim, search and replace without mistakes, and change a live server config with a backup, a check and a way back.
By the end of this lesson you'll be able to
0 of 6 completeWhy you edit files in the terminal
On a server, almost everything is a text file: nginx's settings, SSH's rules, cron jobs, systemd
units, the app's .env. There's no desktop and no VS Code window, only an SSH session. So
editing a file in the terminal isn't a nice-to-have. It's how most production changes are made,
and how most production mistakes start.
You need two editors:
| Editor | Use it when | Why |
|---|---|---|
| nano | Quick edits, when you're new, when you're nervous | Every shortcut is printed on screen |
| vim | Everything else, and whenever nano isn't installed | It's on every server, every container image with a shell, and every exam |
You'll also see Emacs (powerful, popular with some programmers, rarely installed on servers)
and graphical editors like VS Code, which can edit server files over SSH with its Remote extension.
Both are fine. Neither replaces knowing nano and vim, because the server you're fixing at 2 a.m.
may have only vi.
nano: the editor you can use in a minute
$ nano notes.txt # open (or create) a file $ nano -l notes.txt # with line numbers, which you'll want when an error says "line 42" $ nano +42 /etc/hosts # open with the cursor on line 42
You type, and the text goes in. The keys that matter:
| Do this | Press |
|---|---|
| Save | Ctrl+O, then Enter to keep the name |
| Quit | Ctrl+X (it asks to save if anything changed: Y or N) |
| Search | Ctrl+W, type, Enter. Alt+W finds the next match |
| Search and replace | Ctrl+\ (or Alt+R) |
| Go to a line | Ctrl+_ (newer versions show it as ^/) |
| Cut / paste a whole line | Ctrl+K / Ctrl+U |
| Undo / redo | Alt+U / Alt+E |
| Where am I? | Ctrl+C shows the line and column |
vim: learn the modes and the keys follow
vim feels strange for one reason: the same key does different things depending on the mode.
In normal mode keys are commands (dd deletes a line). In insert mode keys type text. Once
you always know which mode you're in, vim stops being scary.
$ vim app.conf # vim opens in NORMAL mode: keys are commands, not text $ vimtutor # the built-in 30-minute tutorial. Worth every minute
On most servers vi starts vim (or a small version of it), so everything here works with vi too.
Getting into insert mode, and out
| Key | Starts typing |
|---|---|
i / a | before / after the cursor |
I / A | at the start / end of the line |
o / O | on a new line below / above |
Esc | back to normal mode, always |
Moving around (normal mode)
| Keys | Moves |
|---|---|
h j k l | left, down, up, right (the arrow keys work too) |
w / b | next word / back a word |
0 / $ | start / end of the line |
gg / G | first / last line of the file |
42G or :42 | line 42 |
Put a number in front of a command to repeat it: 5j moves down five lines, 3dd deletes three.
Changing text (normal mode)
| Keys | Does |
|---|---|
x | delete the character under the cursor |
dd / yy | cut / copy the whole line ("yank") |
dw | delete to the end of the word |
p / P | paste below / above |
u / Ctrl+R | undo / redo |
v / V | select characters / whole lines (visual mode), then d or y |
. | repeat the last change |
Search and replace
/listen search forward for "listen" n = next match, N = previous ?listen search backward :noh clear the highlight :s/8080/9090/ first match on this line only :s/8080/9090/g every match on this line :%s/8080/9090/g every match in the whole file :%s/8080/9090/gc every match, asking y/n/a/q for each one :10,20s/http:/https:/g only lines 10 to 20
% means every line, g every match on a line, c confirm. Use gc on anything that matters:
:%s/80/8080/g would also turn 8000 into 808000. If a replace goes wrong, one u undoes all of
it. In nano, Ctrl+\ asks the same questions: what to find, what to replace it with, then Y, N or A
(all) for each match.
Save and quit safely
| In vim (normal mode) | Does |
|---|---|
:w | save |
:wq or :x or ZZ | save and quit |
:q | quit (refuses if there are unsaved changes) |
:q! or ZQ | quit and throw away the changes |
:w! | save even if the file is marked read-only (when you're allowed to) |
:w newname.conf | save a copy under another name |
Three things go wrong sooner or later. Here's what each one means:
Editing a server's config without breaking it
One typo in nginx.conf or sshd_config, followed by a restart, can take a site down or lock
you out of the server. Experienced engineers follow the same five steps every time:
$ sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak-$(date +%F) # 1. back up $ sudoedit /etc/nginx/nginx.conf # 2. edit $ sudo nginx -t # 3. validate nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful $ sudo systemctl reload nginx # 4. reload, not restart $ curl -sI http://localhost | head -n 1 # 5. verify HTTP/1.1 200 OK
sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak-$(date +%F)sudorun the next command as rootcpcopy files
sudo systemctl reload nginxsystemctlcontrol systemd services
curl -sI http://localhost | head -n 1curlmake an HTTP request-ssilent: no progress bar-Iheaders only|pipe: send output to the next commandheadshow the first lines-nhow many lines
Most services have a check for step 3:
| Service | Check before you reload |
|---|---|
| nginx | sudo nginx -t |
| SSH | sudo sshd -t (prints nothing when the file is fine) |
| sudoers | always edit with sudo visudo, which refuses to save a broken file; sudo visudo -c checks it |
| systemd unit | systemd-analyze verify myapp.service |
If step 3 or step 5 fails, roll back with
sudo cp /etc/nginx/nginx.conf.bak-2026-10-13 /etc/nginx/nginx.conf (use your backup's date),
then reload again. reload keeps the running service up while it reads the new config; restart stops it
first, which is the moment a broken file takes you down.
When an editor isn't the right tool
Scripts and pipelines can't press Ctrl+O. To create a file without opening an editor:
$ cat > ~/lab2/app.conf <<'EOF' port = 8080 log_level = info EOF $ cat ~/lab2/app.conf port = 8080 log_level = info
cat > ~/lab2/app.conf <<'EOF'catprint a file>write output to a file, replacing it
<<'EOF' (a "heredoc") feeds the lines up to EOF into the command. The quotes stop the shell
from expanding $VARIABLES inside it.
For a file only root may write, sudo cat > /etc/myapp/app.conf fails: the > belongs to your
own shell, which runs without sudo. Let tee do the writing instead:
$ sudo tee /etc/myapp/app.conf > /dev/null <<'EOF' port = 8080 EOF
sudo tee /etc/myapp/app.conf > /dev/null <<'EOF'sudorun the next command as rootteewrite input to a file and pass it on>write output to a file, replacing it
For one-line changes in scripts (sed -i 's/8080/9090/' app.conf), see the Text processing
lesson.
Choosing your default editor
git commit, crontab -e, kubectl edit and many other tools open an editor for you. They pick
it from these variables:
$ echo 'export EDITOR=nano' >> ~/.bashrc # or vim $ echo 'export VISUAL=nano' >> ~/.bashrc # some tools check VISUAL first $ source ~/.bashrc $ git config --global core.editor nano # git's own setting wins over both
echo 'export EDITOR=nano' >> ~/.bashrcechoprint text>>append output to a file
git config --global core.editor nanogitversion controlconfigread or set settings--globalfor your user, in every repo
On Ubuntu and Debian, sudo update-alternatives --config editor sets the system-wide default,
which is why visudo opens nano there. visudo and sudoedit also follow SUDO_EDITOR,
VISUAL and EDITOR.
Change an app's port, safely
Your team is moving the app from port 8080 to 9090. The port appears in two places in ~/lab2/app.conf. Create the file, back it up, change every 8080 to 9090 in vim while confirming each match, and prove exactly what changed.
~/lab2/app.confReveal one safe solution
$ mkdir -p ~/lab2 && cat > ~/lab2/app.conf <<'EOF' name = payments port = 8080 log_level = info health_url = http://localhost:8080/health EOF $ cp ~/lab2/app.conf ~/lab2/app.conf.bak # 1. back up $ vim ~/lab2/app.conf # 2. edit: :%s/8080/9090/gc, y, y, then :wq $ grep -n 9090 ~/lab2/app.conf # 3. check the new value is there 2:port = 9090 4:health_url = http://localhost:9090/health $ diff ~/lab2/app.conf.bak ~/lab2/app.conf # 4. prove nothing else changed 2c2 < port = 8080 --- > port = 9090 4c4 < health_url = http://localhost:8080/health --- > health_url = http://localhost:9090/health
mkdir -p ~/lab2 && cat > ~/lab2/app.conf <<'EOF'mkdirmake a directory-pcreate missing parents, no error if it exists&&run the next command only if this one workedcatprint a file>write output to a file, replacing it
cp ~/lab2/app.conf ~/lab2/app.conf.bakcpcopy files
grep -n 9090 ~/lab2/app.confgrepfind lines that match a pattern
diff is your proof: exactly two lines changed, and they're the right two. If you'd typed
:%s/80/90/g by mistake, diff would show more changed lines, and cp app.conf.bak app.conf puts
everything back.
Try it yourself
0 of 6 steps doneRun each task in your own account or terminal, then tick it off.
Quick check
You're in vim, you've typed some text, and now you want to save and quit. What do you press?
Official reading for this lesson
- Docsnano cheatsheet (every shortcut on one page)GNU nano
- DocsThe nano manualGNU nano
- Guidevim user manual, chapter 1 (and vimtutor)vimhelp.org
- Docsvim: changing text, including :substitutevimhelp.org
- Docsvim: swap files and recoveryvimhelp.org
- Docssudo and sudoedit manualUbuntu manpages
- DocsGit configuration: core.editorgit-scm.com
- GuideLPIC-1 exam 101, objective 103.8: basic file editingLinux Professional Institute
Or get every quiz answer right and it completes itself.