Operating Running Systems: Boot Targets, Processes, Logs, Tuning
- Explain how systemd targets replaced runlevels, and switch the default and live target
- Recover a lost root password from the boot loader — including the SELinux relabel step
- Read the system journal by boot, priority, and unit, and make it persistent
- Find CPU- and memory-hungry processes with ps and top, and stop or reload them with the right signal (TERM, KILL, HUP)
- Adjust process priority with nice/renice and pick a tuned profile for a workload
Targets replaced runlevels
Understand what a systemd target is and how it maps onto the old runlevel idea.
Old SysV init had runlevels — a single number (0-6) that described the machine's mode: 3 was multi-user text, 5 was multi-user with a graphical login, 0 was halt, 6 was reboot. systemd dropped the magic numbers in favour of named targets.
A target is just a special unit (
name.target) that groups other units. Think of a target as a stage manager's cue sheet: calling a cue doesn't itself do the work — it tells everyone in that scene to take their places.graphical.targetcues up everything inmulti-user.targetplus the display manager.The two you must know cold:
- `multi-user.target` — full text/networked system, no GUI. The old runlevel 3.
- `graphical.target` — everything in multi-user plus a graphical login. The old runlevel 5.
systemctl get-defaultgraphical.target
get-defaultprints the target the machine boots into. Here it boots to a graphical login. This is the single source of truth for "what mode does this box come up in" — there is no runlevel file to read anymore.Check yourself
Which target is the modern equivalent of the old runlevel 3 (multi-user, networked, no GUI)?
Switch the target: set-default vs isolate
Change the boot-time target and the live target — and keep the two straight.
There are two different questions, exactly like the active/enabled split from the systemctl lesson:
- What target should the machine boot into next time? →
set-default - What target should the machine switch to right now, without rebooting? →
isolate
set-defaultrewrites the/etc/systemd/system/default.targetsymlink.isolatestops everything not part of the named target and starts everything that is — switching the running system live.sudo systemctl set-default multi-user.targetRemoved /etc/systemd/system/default.target. Created symlink /etc/systemd/system/default.target → /usr/lib/systemd/system/multi-user.target.
- set-default
- Change which target the system boots into. Persists across reboots.
- get-default
- Print the current default target (read-only).
The output shows the symlink being repointed: the box will now boot to text mode at the next reboot. Nothing changes in the current session — set-defaultis purely about future boots.sudo systemctl isolate multi-user.target- isolate
- Switch the live system to this target now, stopping units not wanted by it.
- rescue.target
- Single-user-style maintenance mode with a root shell; minimal services.
- emergency.target
- Even more minimal than rescue — only the root filesystem mounted, for deep repair.
isolatechanges the running system immediately — a graphical session would drop to a text console here, no reboot needed. Output is silent on success. Pair it withset-defaultwhen you want the change to both take effect now and stick after a reboot. Silent — switches the RUNNING system to the multi-user (text) target immediately, without a reboot. set-default changes the BOOT target; isolate changes it now. Two different questions, same runtime-vs-persistent split.Check yourself
You need the machine to drop to a text console immediately, without rebooting. Which subcommand do you use?
- What target should the machine boot into next time? →
Reset a lost root password
Walk the full boot-loader recovery procedure, including the step everyone forgets.
No root password, no record of it, and you're locked out. Because you have physical (or console) access to the boot loader, you can interrupt boot and get a root shell before the system finishes coming up. The trick is
rd.break, which drops you to a shell in the initramfs — the tiny early-boot environment — while the real root filesystem is still mounted read-only under/sysroot.The procedure has a sting in the tail. On a SELinux-enforcing system (the RHCSA default),
passwdwrites to/etc/shadow, and a file written from the initramfs gets the wrong SELinux label. If you skip the relabel, the system boots butsshd/login can't read shadow correctly and you're locked out again. The fix is onetouch— and it's the single most-forgotten step on the exam.Step 1 — interrupt the boot loader. At the GRUB menu, press
eto edit the default entry, find the line that starts withlinux(the kernel line), and appendrd.breakto its end:linux ($root)/vmlinuz-... root=/dev/mapper/rhel-root ro crashkernel=auto rd.breakrd.breaktells the initramfs to stop and hand you a shell before it pivots to the real root filesystem. PressCtrl+xto boot this edited entry. The edit is one-shot — it is not saved, so a normal reboot later is unaffected. Edited at the GRUB boot line (e). rd.break drops you to an initramfs shell BEFORE the real root mounts — the entry point for password recovery. No stdout; the system boots into the emergency shell.Step 2 — remount the real root read-write. You land at a
switch_root:/#prompt. The real filesystem is mounted read-only at/sysroot, so you must remount it writable before you can change anything:Example — not runnable on your lab box
mount -o remount,rw /sysroot- -o remount
- Re-apply mount options to an already-mounted filesystem without unmounting it.
- rw
- Mount read-write — the initramfs mounts /sysroot ro by default, blocking edits.
Without this, passwdwould fail with a read-only filesystem error./sysrootis your real/seen from inside the early-boot environment. At the rd.break shell the real root is mounted read-only under /sysroot — remount it read-write so you can actually change the password.Step 3 — chroot into the real system, then set the password.
chroot /sysrootmakes/sysrootbecome/for the shell, sopasswdedits the real/etc/shadow:Example — not runnable on your lab box
chroot /sysroot passwdChanging password for user root. New password: Retype new password: passwd: all authentication tokens updated successfully.
After chroot, your prompt becomes a normalsh-5.1#.passwdwith no username targets root. "all authentication tokens updated successfully" confirms/etc/shadowwas rewritten — but with the wrong SELinux label, which the next step fixes.Step 4 — the step everyone forgets: schedule an SELinux relabel. Then exit out and let the boot continue:
touch /.autorelabel exit exit/.autorelabelis a flag file. On the next boot SELinux sees it, relabels every file on the filesystem to its correct context (fixing the freshly-written shadow file), removes the flag, and reboots once more. The twoexits leave the chroot and then the initramfs shell, resuming boot. The step everyone forgets: /.autorelabel tells SELinux to relabel the whole filesystem on next boot (the password file's context changed). Skip it on an enforcing system and you can't log in even with the new password. The two exits leave the chroot and resume boot.Check yourself
After
chroot /sysrootandpasswd, what command makes sure the system can still authenticate at the next boot on an SELinux-enforcing host?Read the journal: journalctl
Query logs by boot, priority, and unit — and make the journal survive reboots.
systemd collects logs centrally in the journal, and you read it with
journalctl. Instead of grepping a dozen text files under/var/log, you ask the journal structured questions: which boot? how severe? which service? since when? Think of it as a flight recorder with a query language — every event is timestamped and tagged, so you filter instead of scroll.The four filters that cover almost every troubleshooting task:
-b→ just the current boot (or a past boot)-p→ by priority (severity)-u→ by unit (one service)--since/--until→ by time window
journalctl -b -p err --no-pagerJun 23 10:21:04 host kernel: ACPI Error: No handler for Region [ECRM] Jun 23 10:22:11 host chronyd[742]: Could not open /etc/chrony.keys Jun 23 10:22:13 host sshd[915]: error: Bind to port 22 on 0.0.0.0 failed: Address already in use
- -b
- Limit to the current boot.
-b -1would show the previous boot. - -p err
- Show priority err (3) and worse — err, crit, alert, emerg. Hides info/notice noise.
- --no-pager
- Print straight to stdout instead of opening
less— needed for piping or scripting.
This is the fastest "what's broken right now" view: only this boot, only error-level and above. The three lines here point at three separate problems — a firmware quirk, a missing chrony key file, and an SSH port conflict — without wading through thousands of info lines. journalctl -u sshd --since "today" --no-pagerJun 23 10:22:13 host sshd[915]: Server listening on 0.0.0.0 port 22. Jun 23 10:31:48 host sshd[1402]: Accepted publickey for webadmin from 192.0.2.40 port 51022
- -u sshd
- Only log entries from the sshd unit — scopes the noise down to one service.
- --since "today"
- Time filter. Accepts "today", "2026-06-23 09:00", "1 hour ago", etc.
Combining -uwith--sinceanswers "what has this one service done recently?" — here, sshd started and accepted one key-based login. You can stack filters:-u sshd -p err -bis "sshd errors this boot."By default the journal lives in
/run/log/journal, which is memory-backed and wiped on reboot — sojournalctl -b -1(last boot) shows nothing. To keep logs across reboots, make the journal persistent by creating its on-disk directory:sudo mkdir -p /var/log/journal && sudo systemctl restart systemd-journaldOnce /var/log/journalexists, journald writes there permanently and past boots survive. (SettingStorage=persistentin/etc/systemd/journald.confis the explicit, configuration-driven way to do the same thing.) After this,journalctl --list-bootsshows multiple boots you can query individually. Silent — creates the persistent journal directory and restarts journald to use it. By default the journal is in-memory (RAM) and lost on reboot; this makes it survive, so journalctl -b -1 can read the PREVIOUS boot.Check yourself
Which journalctl flag restricts output to a single service's log entries — for example only sshd?
Find the hog, then pick the right signal: ps, top, kill
Identify the process eating CPU or memory, then stop it, or reload it, with the correct signal.
"The box is slow" is a question about which process.
ps auxprints every process with the columns that answer it: `%CPU` (share of a CPU it has used), `%MEM` and `RSS` (resident memory, in KiB, actually held in RAM), and the `PID` you will act on. Sort by the column you care about and the culprit floats to the top. Here is a snapshot from a loaded database server:Example — not runnable on your lab box
ps aux --sort=-%mem | head -4USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND postgres 3120 98.0 6.5 911768 524288 ? Rs 08:01 42:11 postgres: app appdb [local] SELECT app 3470 4.0 3.0 1258292 245760 ? Sl 08:02 12:40 ruby /srv/app/bin/worker root 980 0.0 0.2 59392 17612 ? Ss 07:58 0:05 nginx: master process /usr/sbin/nginx
- a / u / x
- All users' processes, user-oriented columns (USER, %CPU, %MEM, RSS…), and processes with no terminal (daemons).
- --sort=-%mem
- Sort by %MEM, highest first (the leading - means descending). Use --sort=-%cpu or --sort=-rss for the other rankings.
PID 3120 is the memory hog (RSS 524288 KiB, 512 MiB, 6.5% of RAM) and also the CPU hog at 98%. VSZis virtual size, which counts memory merely reserved, so rank memory by RSS, not VSZ. The header line is not a process: when you post-process this output withawkorsort, skip line 1.topis the live version: it refreshes every few seconds, and inside it `P` sorts by CPU and `M` by memory (qquits).top -bn1prints one batch snapshot you can pipe or save.Once you have the PID, the action is a signal, and choosing the signal is the skill.
kill -llists them all, andman 7 signalexplains what each one means and whether a process can catch it. Three matter daily:- `SIGTERM` (15), the default for
kill PID: please exit. The process can clean up first. - `SIGKILL` (9): the kernel removes the process at once. It can't be caught or cleaned up after, so it's the last resort for a process that ignored TERM.
- `SIGHUP` (1): for a daemon, the convention is re-read your configuration. The process keeps running with the new settings.
pgrep -af NAMEfinds PIDs by name, andpkill NAMEsignals by name.( sleep 300 & kill -TERM $!; wait $!; echo "exit status: $?" )exit status: 143
- -TERM
- The signal to send; also written -15 or -s TERM. Plain
kill PIDsends TERM. - $!
- The PID of the most recent background job: the sleep.
Start a stand-in runaway, stop it with SIGTERM, and read how it died. An exit status above 128 means killed by a signal: 143 = 128 + 15, so SIGTERM. Akill -9would give 137 (128 + 9). The parentheses run the demo in a subshell, so no job-control chatter clutters the output.( bash -c 'trap "echo reloaded config" HUP; while :; do sleep 1; done' & sleep 1; kill -HUP $!; sleep 2; kill -0 $! && echo "still running"; kill $! )reloaded config still running
- -HUP
- Send SIGHUP (signal 1); also written -1 or -s HUP.
- kill -0 PID
- Send no signal; just test whether the PID exists (exit 0 if it does).
The background loop plays a daemon that handles HUP the way real ones (sshd, nginx, rsyslog) do: it reloads and carries on. kill -0proves it survived the HUP; the finalkillsends TERM to clean up. Send that same daemonkill -9instead and it is gone, its config never re-read.Check yourself
A logging daemon must re-read its configuration without stopping. Which signal do you send it?
Priority and tuning: nice, renice, tuned-adm
Bias CPU scheduling for a process and pick a workload profile for the whole system.
Two levers control performance. One is per-process: the nice value, a politeness score from
-20(greedy, highest priority) to+19(very polite, lowest). Higher niceness = nicer to others = less CPU for itself. Think of it as boarding-group numbers: a low number boards first and grabs the overhead bin; a high number waits.The other is system-wide: tuned ships ready-made profiles (
throughput-performance,powersave,virtual-guest,balanced...) that twist dozens of kernel and hardware knobs at once for a given workload — so you pick a profile instead of hand-tuning sysctls.Example — not runnable on your lab box
nice -n 10 ./batch-import.sh- -n 10
- Start the command with niceness +10 — lower priority, yields CPU to normal tasks.
nicelaunches a new command at a chosen niceness. +10 means "run this batch job in the background politely, don't starve interactive work." Only root can request a negative niceness (higher-than-normal priority). Silent — starts the job with a lowered CPU priority (niceness 10; range -20 highest to 19 lowest). renice adjusts a running process; nice sets it at launch, so a heavy batch job yields to interactive work.sudo renice -n 5 -p 22072207 (process ID) old priority 0, new priority 5
- renice
- Change the niceness of an ALREADY-running process.
- -n 5
- The new niceness value to set.
- -p 2207
- Target the process with this PID.
reniceadjusts a process that's already running — here PID 2207 goes from the default 0 to +5, easing its CPU pressure on the rest of the box. The output confirms the old and new values. Use this on a runaway job you don't want to kill outright.Example — not runnable on your lab box
sudo tuned-adm active; sudo tuned-adm profile throughput-performanceCurrent active profile: balanced
- active
- Show which tuned profile is currently applied.
- profile <name>
- Switch to the named profile, applying its tuning immediately and at boot.
- recommend
- Ask tuned to suggest a profile based on the detected hardware/role.
First command reports the live profile ( balanced); the second switches tothroughput-performance— the profile for servers that should favour raw throughput over power savings. The change applies now and persists.tuned-admis the RHCSA-blessed way to tune; you name a profile rather than editing kernel parameters by hand.Check yourself
A nice value of +19 versus -20 — which one gives a process the LOWEST priority (least CPU when the system is busy)?
Try it in your live terminal
Run the safe, read-only versions of today's commands on the real sandbox to your right.
You have a real root shell. These are read-only — they inspect state without changing the boot target or resetting any password, so they're safe to run as-is. (The password-reset procedure deliberately breaks a box if done wrong, so practise that one in a throwaway VM, not here.) Run them in order and read what each tells you.
A note on
journalctlhere: this lab host doesn't run systemd's journald as PID 1, sojournalctlmay print nothing (and--list-bootsshows a single volatile entry). Run it to learn its shape — an empty journal means this host logs to flat files under `/var/log`, which is where you'd actually read on a box like this. Thesystemctlreads work as shown.systemctl get-defaultWhat target does this machine boot into? multi-user.target= text,graphical.target= GUI. Read-only — it changes nothing.journalctl -p err -b --no-pager | tailThe most recent error-and-worse log lines from this boot. If it's empty, the box has logged no errors since boot — good news. journalctl --list-boots --no-pagerLists the boots the journal still has records for. One line means the journal is volatile (memory-only); several means it's persistent. ps aux --sort=-%cpu | head -5The top CPU consumers on this sandbox right now, header first. Find the PID, %CPU, %MEM and RSS columns, then re-run with --sort=-%memand see whether the same process tops both lists.Example — not runnable on your lab box
tuned-adm activeWhich tuned profile is applied right now. Likely balancedorvirtual-gueston a VM. Read-only inspection.
- Targets replaced runlevels.
multi-user.target(text) andgraphical.target(GUI) are the two to know;get-default/set-defaultcontrol the boot target,isolateswitches the live one. - Password reset =
rd.break→mount -o remount,rw /sysroot→chroot /sysroot→passwd→ `touch /.autorelabel`. The relabel is the step that locks you out if skipped on an enforcing system. journalctlfilters:-b(boot),-p(priority),-u(unit),--since(time). Create/var/log/journalto make logs persist across reboots.- Find hogs with
ps aux --sort=-%cpu/--sort=-%mem(rank memory by RSS) ortop(P/M). Signal with intent:SIGTERM(15) to stop politely,SIGKILL(9) only as a last resort,SIGHUP(1) to make a daemon reload its config. nice/renicebias CPU scheduling per process (higher value = lower priority);tuned-admapplies a whole-system workload profile.
| Command | What it does | Watch for |
|---|---|---|
systemctl get-default | Show the boot target | Read-only — never changes anything |
systemctl set-default multi-user.target | Boot to text mode NEXT boot | Does not change the current session |
systemctl isolate multi-user.target | Switch the LIVE system to text now | No reboot; drops a GUI session immediately |
touch /.autorelabel | Schedule SELinux relabel after password reset | Forgetting it locks you out again |
journalctl -b -p err | This boot's errors only | -b -1 for the previous boot needs persistent journal |
journalctl -u sshd --since today | One unit's log over a time window | Stack filters: -u, -p, -b combine |
ps aux --sort=-%mem | head | Rank processes by memory (or -%cpu by CPU) | Rank memory by RSS, not VSZ; skip the header |
kill -HUP <pid> | Make a daemon reload its config, still running | TERM (15) stops politely; KILL (9) is the last resort |
renice -n 5 -p <pid> | Lower a running process's priority | Only affects CPU under contention |
tuned-adm profile throughput-performance | Apply a workload tuning profile | tuned-adm active to confirm it stuck |
