DevOps.Academy
CoursesCurriculumPricing

    Loading…

    ↑ ↓ move · ↵ open · Ctrl K anywhere

    ◔My learningSign in
    DevOps.Academy

    Free, hands-on DevOps lessons. Made with care in India.

    Learn

    All coursesLinux roadmapCurriculumMy learning

    Academy

    PricingCertificates

    Legal

    PrivacyTermsContact

    devops.rajeev.pro

    Linux & BashLesson 2 of 16
    Your progress · 0%
    On this page
    1. Why you edit files in the terminal
    2. nano: the editor you can use in a minute
    3. vim: learn the modes and the keys follow
    4. Search and replace
    5. Save and quit safely
    6. Editing a server's config without breaking it
    7. When an editor isn't the right tool
    8. Choosing your default editor
    ↖ Course roadmap
    Lesson 2 · Editing files

    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.

    30 min read · beginner · hands-on

    By the end of this lesson you'll be able to

    0 of 6 complete

    Why 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:

    EditorUse it whenWhy
    nanoQuick edits, when you're new, when you're nervousEvery shortcut is printed on screen
    vimEverything else, and whenever nano isn't installedIt'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
    

    nano prints its own cheat sheet. ^O means Ctrl+O; M-U means Alt+U.© Diagram: DevOps Academy

    nano prints its own cheat sheet. ^O means Ctrl+O; M-U means Alt+U.© Diagram: DevOps Academy

    You type, and the text goes in. The keys that matter:

    Do thisPress
    SaveCtrl+O, then Enter to keep the name
    QuitCtrl+X (it asks to save if anything changed: Y or N)
    SearchCtrl+W, type, Enter. Alt+W finds the next match
    Search and replaceCtrl+\ (or Alt+R)
    Go to a lineCtrl+_ (newer versions show it as ^/)
    Cut / paste a whole lineCtrl+K / Ctrl+U
    Undo / redoAlt+U / Alt+E
    Where am I?Ctrl+C shows the line and column
    Keep a backup while you learn

    nano -B file saves the previous version as file~ every time you save. On a server, you'll usually make the backup yourself instead (see below), but it's a good habit on your own machine.

    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.

    Everything goes through normal mode. Lost? Press Esc, and you're home.© Diagram: DevOps Academy

    Everything goes through normal mode. Lost? Press Esc, and you're home.© Diagram: DevOps Academy
    $ 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

    KeyStarts typing
    i / abefore / after the cursor
    I / Aat the start / end of the line
    o / Oon a new line below / above
    Escback to normal mode, always

    Moving around (normal mode)

    KeysMoves
    h j k lleft, down, up, right (the arrow keys work too)
    w / bnext word / back a word
    0 / $start / end of the line
    gg / Gfirst / last line of the file
    42G or :42line 42

    Put a number in front of a command to repeat it: 5j moves down five lines, 3dd deletes three.

    Changing text (normal mode)

    KeysDoes
    xdelete the character under the cursor
    dd / yycut / copy the whole line ("yank")
    dwdelete to the end of the word
    p / Ppaste below / above
    u / Ctrl+Rundo / redo
    v / Vselect 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.

    What :%s/8080/9090/gc really looks like: vim highlights each match and asks before changing it. y replaces, n skips, a replaces the rest, q stops.© Screenshot: DevOps Academy

    What :%s/8080/9090/gc really looks like: vim highlights each match and asks before changing it. y replaces, n skips, a replaces the rest, q stops.© Screenshot: DevOps Academy

    Save and quit safely

    In vim (normal mode)Does
    :wsave
    :wq or :x or ZZsave and quit
    :qquit (refuses if there are unsaved changes)
    :q! or ZQquit and throw away the changes
    :w!save even if the file is marked read-only (when you're allowed to)
    :w newname.confsave a copy under another name

    Three things go wrong sooner or later. Here's what each one means:

    Permission denied (or vim's E212) when saving

    The file belongs to root. Don't chmod it. Quit with :q! and edit it the right way: sudoedit /etc/nginx/nginx.conf. sudoedit copies the file, opens the copy in your editor as you, and writes it back as root when you quit. Your editor never runs as root, so your plugins and settings can't touch system files by accident.

    E325: ATTENTION. Found a swap file

    vim keeps unsaved work in a hidden file like .app.conf.swp. You see this when someone else has the file open right now, or an earlier session crashed (a dropped SSH connection, for example). If someone is editing: press O to open read-only and talk to them. If it crashed: press R to recover, check the text, :w, then delete the old swap file (rm .app.conf.swp).

    $'\r': command not found

    The file was saved on Windows, so every line ends with an invisible \r. file script.sh reports "with CRLF line terminators". Fix it with sed -i 's/\r$//' script.sh (or dos2unix), or in vim with :set ff=unix then :w.

    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:

    Never restart on a config you haven't validated. Keep the backup until you've verified.© Diagram: DevOps Academy

    Never restart on a config you haven't validated. Keep the backup until you've verified.© Diagram: DevOps Academy
    $ 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 root
    • cpcopy files
    sudo systemctl reload nginx
    • systemctlcontrol systemd services
    curl -sI http://localhost | head -n 1
    • curlmake an HTTP request
    • -ssilent: no progress bar
    • -Iheaders only
    • |pipe: send output to the next command
    • headshow the first lines
    • -nhow many lines

    Most services have a check for step 3:

    ServiceCheck before you reload
    nginxsudo nginx -t
    SSHsudo sshd -t (prints nothing when the file is fine)
    sudoersalways edit with sudo visudo, which refuses to save a broken file; sudo visudo -c checks it
    systemd unitsystemd-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.

    In production: keep your SSH session open

    When you change sshd_config, run sudo sshd -t, reload SSH, then open a second SSH session to check you can still log in, before you close the first one. If the new config locks you out, the old session is your way back in.

    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 root
    • teewrite 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' >> ~/.bashrc
    • echoprint text
    • >>append output to a file
    git config --global core.editor nano
    • gitversion control
    • configread 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.

    ⌘
    Mini mission

    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.

    Input: ~/lab2/app.conf (port 8080, health check on 8080)Output: ~/lab2/app.conf
    Reveal 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 worked
    • catprint a file
    • >write output to a file, replacing it
    cp ~/lab2/app.conf ~/lab2/app.conf.bak
    • cpcopy files
    grep -n 9090 ~/lab2/app.conf
    • grepfind 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 done

    Run each task in your own account or terminal, then tick it off.

    Knowledge check

    Quick check

    Question 1 of 6

    You're in vim, you've typed some text, and now you want to save and quit. What do you press?

    Go deeper

    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.

    ← PreviousThe shell and the filesystemNext lesson →Shell basics: PATH, variables, streams and sudo