Linux: System Configuration
Table of Contents
- What “System Configuration” Covers
- System Logging
- Users and Groups
- System Time
- Scheduling Tasks
- Cheat Sheet
1. What “System Configuration” Covers
Besides the kernel and services started at boot, a working Linux system depends on:
- Configuration files that system libraries read to get server and user information (mostly in
/etc) - Server programs (daemons) that start at boot
- Configuration utilities used to adjust those daemons and files
This note covers four areas built on those pieces: logging, users, time, and periodic tasks.
2. System Logging
Anatomy of a log message
A log message usually contains a timestamp, the host, the process name, its process ID (PID), and the message itself:
Aug 19 17:59:48 duplex sshd[484]: Server listening on 0.0.0.0 port 22.
└──────┬──────┘ └──┬──┘ └─┬─┘└┬┘ └───────────────┬───────────────┘
Timestamp Host Process PID Message
journald and syslog
On systemd distributions, systemd-journald collects messages from the kernel, services (their stdout/stderr) and the syslog API, and stores them in a binary journal. Some systems also run a traditional syslog daemon (e.g. rsyslogd) that writes plain-text files in /var/log.
┌─────────┐ ┌──────────────────┐ ┌─────────────┐
│ Kernel │ │ Services (stdout,│ │ syslog() API│
│ (kmsg) │ │ stderr) │ │ (/dev/log) │
└────┬────┘ └────────┬─────────┘ └──────┬──────┘
└────────────────┼───────────────────┘
▼
┌───────────────────┐
│ systemd-journald │
└─────────┬─────────┘
┌────────────┴─────────────┐
▼ ▼ (optional forwarding)
┌──────────────────────┐ ┌─────────────────────┐
│ Binary journal │ │ rsyslogd │
│ /var/log/journal │ │ → text files in │
│ (or /run/log/journal)│ │ /var/log/… │
└──────────┬───────────┘ └─────────────────────┘
▼
journalctl
- If
/var/log/journalexists, the journal is persistent. Otherwise it may only live in/run/log/journaland is lost at reboot. This is controlled byStorage=in/etc/systemd/journald.conf. - Plain-text logs are kept from growing forever by
logrotate. The journal limits its own size withSystemMaxUse=.
Priorities (severity levels)
| Level | Name | Level | Name |
|---|---|---|---|
| 0 | emerg | 4 | warning |
| 1 | alert | 5 | notice |
| 2 | crit | 6 | info |
| 3 | err | 7 | debug |
journalctl -p err shows levels 0–3 (everything at err or more severe).
journalctl
With no options, journalctl shows the whole journal, oldest first, in a pager.
Filter by time
journalctl -S -4h # since 4 hours ago
journalctl -S 06:00:00 # since 06:00 today
journalctl -S 2026-09-27 # since a date
journalctl -S '2026-09-27 11:30:00' # since date + time (quote it)
journalctl -S 2026-09-27 -U 2026-09-28 # -U = until
Filter by boot
journalctl -b # current boot
journalctl -b -1 # previous boot
journalctl -r -b -1 # previous boot, newest first (-r = reverse)
journalctl --list-boots # all boots in the journal
Filter by unit, field and content
journalctl -u cron.service # one unit (= --unit=cron.service)
journalctl _PID=1 # match a journal field: messages from PID 1
journalctl -N # list all field names in the journal
journalctl -F _SYSTEMD_UNIT # list all values of one field
journalctl -g 'kernel.*memory' # grep messages by regex
journalctl -k # kernel messages only (like dmesg)
journalctl -p err # priority err and worse
-g is case-insensitive when the pattern is all lowercase.
Output and maintenance
journalctl -f # follow live (like tail -f)
journalctl -e # jump to the end
journalctl -o verbose # show all fields of each entry
journalctl -o json-pretty # structured output
journalctl --disk-usage # space used by the journal
sudo journalctl --vacuum-time=2weeks # delete entries older than 2 weeks
sudo journalctl --vacuum-size=500M # shrink the journal to 500 MB
Filters combine: journalctl -u sshd -b -p warning -S -1h.
3. Users and Groups
The kernel only knows numeric user IDs (UIDs) and group IDs (GIDs). User-space files map those numbers to names and store login details.
| File | Contents | Readable by |
|---|---|---|
/etc/passwd | Users: name, UID, GID, home, shell | Everyone |
/etc/shadow | Password hashes and aging | root only |
/etc/group | Groups and their extra members | Everyone |
/etc/gshadow | Group passwords | root only |
/etc/passwd
One line per user, seven fields separated by colons:
juser:x:3119:1000:J. Random User:/home/juser:/bin/bash
└─┬─┘ │ └┬─┘ └┬─┘ └──────┬─────┘ └────┬────┘ └───┬───┘
│ │ │ │ │ │ └── Shell
│ │ │ │ │ └───────────── Home directory
│ │ │ │ └────────────────────────── Real name (GECOS)
│ │ │ └───────────────────────────────────── Primary group ID
│ │ └────────────────────────────────────────── User ID
│ └───────────────────────────────────────────── Password
└───────────────────────────────────────────────── Login name
Password field values:
| Value | Meaning |
|---|---|
x | The real hash is in /etc/shadow (the normal case) |
* | The account cannot log in with a password |
| (empty) | No password needed. Dangerous, avoid |
UID ranges:
- 0 is
root. - Below 1000 (usually) are system/service accounts such as
nobodyorsshd. - 1000 and up are regular users.
The exact boundaries come from UID_MIN / SYS_UID_MAX in /etc/login.defs.
/etc/shadow
juser:$y$j9T$...:20358:0:99999:7:::
└─┬─┘ └───┬───┘ └─┬─┘ │ └─┬─┘ │ │││
│ │ │ │ │ │ ││└─ Reserved
│ │ │ │ │ │ │└── Account expiration date
│ │ │ │ │ │ └─── Days of inactivity allowed after expiry
│ │ │ │ │ └───── Days of warning before expiry
│ │ │ │ └───────── Maximum days between changes
│ │ │ └───────────── Minimum days between changes
│ │ └───────────────── Last change (days since 1970-01-01)
│ └───────────────────────── Password hash
└───────────────────────────────── Login name
- The hash prefix shows the algorithm:
$y$= yescrypt (Fedora, recent Debian/Ubuntu),$6$= SHA-512. - A
!or*in the hash field means the password is locked. - View or change aging with
chage -l juserandchage juser.
/etc/group
disk:*:6:juser,beazley
└┬─┘ │ │ └─────┬─────┘
│ │ │ └─── Additional members (comma-separated)
│ │ └─────────── Group ID
│ └───────────── Password (rarely used)
└───────────────── Group name
The member list holds only supplementary members. A user whose primary GID (from /etc/passwd) is this group belongs to it even if their name isn’t listed.
Tools — don’t edit these files by hand
Editing /etc/passwd directly risks corrupting it, and other programs may write to it at the same time. Use the tools instead.
| Task | Command |
|---|---|
| Change your password | passwd |
| Set another user’s password | sudo passwd juser |
| Change login shell | chsh |
| Change GECOS/real name | chfn |
| Create a user | sudo useradd -m -s /bin/bash juser (adduser on Debian is friendlier) |
| Add user to a group | sudo usermod -aG wheel juser (-a is important: without it, all other supplementary groups are removed) |
| Delete a user (+ home) | sudo userdel -r juser |
| Create a group | sudo groupadd devs |
| Add or remove a group member | sudo gpasswd -a juser devs / -d |
| Safely edit the files | sudo vipw, sudo vipw -s (shadow), sudo vigr |
| Show your IDs and groups | id, groups |
| Look up any user or group | getent passwd juser, getent group disk |
getent asks the system’s name service (NSS), so it also finds users from LDAP, SSSD or alternative files, not just /etc/passwd.
Group changes only apply to new logins. Log out and back in, or use newgrp groupname in the current shell.
Becoming another user
| Command | Effect |
|---|---|
su - juser | Start a login shell as juser (needs juser’s password) |
sudo command | Run one command as root (needs your password; rules in /etc/sudoers, edit with visudo) |
sudo -i | Root login shell |
Authentication itself (checking passwords, 2FA, etc.) is handled by PAM (Pluggable Authentication Modules), configured in /etc/pam.d/.
4. System Time
Two clocks
┌──────────────────────────┐ ┌──────────────────────────┐
│ Hardware clock (RTC) │ read at │ System clock (kernel) │
│ battery-backed, keeps │ boot ─────► │ what `date` shows, used │
│ time while powered off │ │ by all programs │
│ (should be set to UTC) │ ◄───── sync │ │
└──────────────────────────┘ hwclock └────────────▲─────────────┘
--systohc │ adjusts gradually
│
┌────────────┴─────────────┐
│ NTP client │
│ chronyd (Fedora/RHEL) or │
│ systemd-timesyncd │
└────────────▲─────────────┘
│
NTP servers (network)
- The kernel keeps time in UTC. Time zones are only applied when time is displayed.
- The system zone is the symlink
/etc/localtime→/usr/share/zoneinfo/Europe/Berlin. - One shell or program can use a different zone through the
TZvariable:TZ=America/New_York date. - An unsynchronized clock drifts. NTP (Network Time Protocol) corrects it by speeding up or slowing down the system clock instead of jumping.
Commands
date # current local time
date -u # current UTC time
timedatectl # time, zone, RTC, NTP status overview
sudo timedatectl set-timezone Europe/Berlin
sudo timedatectl set-ntp true # enable NTP synchronization
timedatectl list-timezones
chronyc tracking # chrony: current offset/accuracy
chronyc sources -v # chrony: NTP servers in use
sudo hwclock --show # read the RTC
sudo hwclock --systohc # copy system clock → RTC
5. Scheduling Tasks
| Tool | Use for |
|---|---|
| cron | Classic recurring jobs |
| systemd timers | Recurring or delayed jobs with systemd features (logging, dependencies, catch-up) |
at / systemd-run | Run something once at a later time |
cron
Each user has a crontab, which cron checks every minute.
crontab -e # edit your crontab
crontab -l # list it
crontab -r # remove it entirely (no confirmation)
Crontab line format:
15 09 * * 1-5 /home/juser/bin/backup.sh
│ │ │ │ │ └── Command
│ │ │ │ └────── Day of week (0–7, 0 and 7 = Sunday; 1-5 = Mon–Fri)
│ │ │ └───────── Month (1–12)
│ │ └─────────── Day of month (1–31)
│ └────────────── Hour (0–23)
└───────────────── Minute (0–59)
| Syntax | Meaning | Example |
|---|---|---|
* | Every value | * * * * * = every minute |
a,b | List | 0 8,20 * * * = 08:00 and 20:00 |
a-b | Range | 0 9 * * 1-5 = weekdays at 09:00 |
*/n | Step | */15 * * * * = every 15 minutes |
@daily, @reboot, … | Shortcuts | @reboot ~/start.sh |
Notes:
- System-wide jobs go in
/etc/crontabor/etc/cron.d/. These files have an extra user field before the command:0 3 * * * root /usr/local/bin/cleanup. - Cron runs with a minimal environment. Use absolute paths, and remember that
%must be escaped as\%. - Output is mailed to the user if mail is set up. Otherwise, redirect it:
>> /tmp/job.log 2>&1.
systemd timer units
A timer is a pair of units: a .timer says when, and a .service with the same name says what.
┌─────────────────────────┐ activates ┌─────────────────────────┐
│ backup.timer │ ────────────► │ backup.service │
│ [Timer] │ │ [Service] │
│ OnCalendar=*-*-* 03:00 │ │ Type=oneshot │
│ Persistent=true │ │ ExecStart=/usr/local/ │
│ [Install] │ │ bin/backup.sh │
│ WantedBy=timers.target │ └─────────────────────────┘
└─────────────────────────┘
/etc/systemd/system/backup.timer:
[Unit]
Description=Nightly backup
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
/etc/systemd/system/backup.service:
[Unit]
Description=Run backup script
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
Enable the timer, not the service:
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers # next/last run of all timers
systemd-analyze calendar 'Mon..Fri 09:15' # test an OnCalendar expression
journalctl -u backup.service # job output is logged automatically
Useful [Timer] options:
| Option | Meaning |
|---|---|
OnCalendar= | Wall-clock schedule (daily, weekly, Mon *-*-* 09:00) |
OnBootSec= | Time after boot |
OnUnitActiveSec= | Interval after the service last started |
Persistent=true | Run a missed job at the next boot (like anacron) |
RandomizedDelaySec= | Random delay to spread load |
Timers vs cron:
- Timers log automatically to the journal, can use unit dependencies and resource limits, and can catch up on missed runs.
- Cron is shorter to write and available everywhere.
User timers go in ~/.config/systemd/user/ and are managed with systemctl --user. To let them run while you’re logged out, run loginctl enable-linger $USER.
One-time jobs
# at (needs the atd service running)
echo "/home/juser/bin/report.sh" | at 22:30
at now + 2 hours # then type commands, finish with Ctrl-D
atq # list pending jobs
atrm 3 # remove job 3
# systemd alternative: transient timer
systemd-run --on-active=30m /home/juser/bin/report.sh
systemd-run --on-calendar='2026-10-05 08:00' --unit=report /usr/bin/touch /tmp/ok
6. Cheat Sheet
| Task | Command |
|---|---|
| Logs from current / previous boot | journalctl -b / journalctl -b -1 |
| Logs of one service | journalctl -u name.service |
| Logs since a time | journalctl -S -4h, journalctl -S '2026-09-27 11:30:00' |
| Follow logs live | journalctl -f |
| Only errors | journalctl -p err |
| Search messages | journalctl -g 'regex' |
| List field names / values | journalctl -N / journalctl -F FIELD |
| Journal size / cleanup | journalctl --disk-usage / --vacuum-size=500M |
| Who am I, which groups | id |
| Look up a user or group | getent passwd user, getent group name |
| Add user to a group | sudo usermod -aG group user |
| Edit passwd/group safely | sudo vipw, sudo vigr |
| Password aging | chage -l user |
| Time and NTP status | timedatectl |
| Set time zone | sudo timedatectl set-timezone Europe/Berlin |
| Edit crontab | crontab -e |
| List systemd timers | systemctl list-timers |
| Test a calendar expression | systemd-analyze calendar 'expr' |
| Run once later | at 22:30 / systemd-run --on-active=30m cmd |