Linux Operating Systems Fundamentals · Lesson 7

Administering the System

User and group accounts, MTA basics and local mail, rsyslog and systemd-journald logging, and maintaining system time with hwclock, timedatectl, ntpd, and chrony.

  • linux
  • users
  • groups
  • email
  • logging
  • journald
  • ntp
  • chrony
  • lpic-1

Lesson purpose

Keeping servers healthy is routine, careful work: create and retire accounts correctly, keep system mail flowing, record what happens in logs and read them, and keep every clock accurate. This lesson covers user and group administration and the files behind it, basic mail transfer agent (MTA) administration, logging with rsyslog and systemd-journald, and keeping time with hardware and software clocks and NTP (ntpd and chrony).

Learning objectives

  • Explain how /etc/login.defs, /etc/default/useradd, and /etc/skel shape account creation, and read the records in /etc/passwd, /etc/shadow, /etc/group, and /etc/gshadow.
  • Create, modify, lock, and delete accounts and groups with useradd, passwd, chage, usermod, userdel, groupadd, groupmod, and groupdel.
  • Describe the MTA, MDA, and MUA roles; send and read local mail; inspect the mail queue; and configure aliases and forwarding.
  • Write rsyslog rules using facilities, priorities, and actions; forward logs to a central server; rotate logs; and create log entries.
  • Configure, view, filter, persist, and maintain the systemd journal.
  • Manage the hardware and software clocks and synchronize time with NTP using ntpd or chrony.

LPIC-1 exam focus

  • 107.1 Manage user and group accounts and related system files
  • 108.1 Maintain system time
  • 108.2 System logging
  • 108.3 Mail Transfer Agent (MTA) basics

1. Users, groups, and discretionary access control

  • Authentication confirms that a person or program is who it claims to be.
  • Discretionary access control (DAC) is the traditional Linux model: access to a file or other object depends on the user’s identity and group memberships, and the owner can change those permissions (Lesson 4).
  • UID and GID. People use names, but Linux uses numbers. The kernel checks the user ID (UID) and group ID (GID).
  • Primary (default) group. Each account has one, recorded in /etc/passwd. New files get this group.
  • Supplementary groups. An account can belong to many other groups, which are listed in /etc/group.
  • Changing the current group. A process has one current group at a time. newgrp groupname starts a shell with a different current group (the account must be a member).
  • Account types:
    • User (normal) accounts are for people. Their UIDs usually start at 1000.
    • System accounts run services (daemons) or special tasks. They have low UIDs, usually no login shell, and root is UID 0.

2. The files that control account creation

useradd reads two configuration files and copies from one directory. Then it writes to four account files.

/etc/login.defs  ─┐
/etc/default/useradd ─┼─► useradd ─► /etc/passwd, /etc/shadow, /etc/group, /etc/gshadow
/etc/skel/  ──────┘               └─► /home/username (if a home directory is created)

/etc/login.defs

/etc/login.defs holds settings for the shadow password suite, which includes useradd, userdel, passwd, and related commands. To see only the active lines:

$ grep -Ev '^(#|$)' /etc/login.defs
Directive Meaning
UID_MIN / UID_MAX Range for new user accounts (typically 1000–60000)
SYS_UID_MIN / SYS_UID_MAX Range for new system accounts (typically 201–999)
GID_MIN / GID_MAX, SYS_GID_* The same ranges for groups
PASS_MAX_DAYS Days until a password must be changed
PASS_MIN_DAYS Days before a password can be changed again
PASS_WARN_AGE Days of warning before a password expires
PASS_MIN_LEN Minimum password length (on most modern systems, PAM enforces password quality instead)
CREATE_HOME yes creates a home directory by default. If it’s missing, the default is no.
USERGROUPS_ENAB yes creates a private group named after each new user
UMASK Permissions mask for new home directories
ENCRYPT_METHOD Password hashing algorithm, such as SHA512 or YESCRYPT

These values apply only to accounts created after you change them.

/etc/default/useradd

$ useradd -D
GROUP=100
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/bash
SKEL=/etc/skel
CREATE_MAIL_SPOOL=yes
$ sudo useradd -D -s /bin/bash       # change a default without editing the file
  • HOME is the base directory for new home directories.
  • SHELL is the default login shell. Some Ubuntu releases set /bin/sh, which links to Dash, so check it before creating accounts.
  • SKEL is the skeleton directory.
  • INACTIVE is the number of days after a password expires before the account is disabled. -1 means never.
  • EXPIRE is the default account expiration date.

/etc/skel

The files in /etc/skel, such as .bashrc, .bash_profile, and .bash_logout, are copied into each new home directory. List them with ls -a /etc/skel. Changing /etc/skel later doesn’t update existing users. You have to copy the changes to them yourself or with a script.

3. Reading /etc/passwd and /etc/shadow

/etc/passwd

This file has one line per account and seven colon-separated fields. Everyone can read it.

Christine:x:1001:1001:Christine B:/home/Christine:/bin/bash
Field Meaning
1 Username
2 Password placeholder. x means the hash is in /etc/shadow.
3 UID
4 Primary GID
5 Comment (GECOS), usually the full name
6 Home directory
7 Login shell. /sbin/nologin or /bin/false blocks interactive logins.
  • /sbin/nologin shows a polite message (which you can customize in /etc/nologin.txt) and disconnects. It’s typical for service accounts.
  • /bin/false disconnects silently.
  • If you ever find password hashes in /etc/passwd, move them to /etc/shadow with pwconv.

/etc/shadow

Only root can read this file. It has one line per account and nine fields.

DAdams:$6$B/zCaNx...:17806:5:30:15:3::
Field Meaning
1 Username (the only field shared with /etc/passwd)
2 Salted password hash. !! or ! means no password has been set; * or ! means no password login is possible; a ! in front of a hash means the account is locked.
3 Date of the last password change, in days since January 1, 1970
4 Minimum days between password changes
5 Maximum days before a password change is required
6 Days of warning before the password expires
7 Inactive days after the password expires before the account is disabled
8 Account expiration date, in days since January 1, 1970
9 Reserved

The hash prefix identifies the algorithm: $6$ is SHA-512, $y$ is yescrypt (the default on current Debian, Ubuntu, and Fedora), and $1$ is old MD5.

Two different expirations:

  • Password expiration (field 5) has a grace period (field 7). The user can still log in with the old password, but must change it immediately. After the grace period, the account is effectively locked.
  • Account expiration (field 8) has no grace period. After that date, the account can’t be used at all. Set it for interns, contractors, and other temporary accounts so a forgotten account can’t be used later.

You don’t need to convert the dates in fields 3 and 8 by hand. chage -l displays them as calendar dates.

4. Creating accounts with useradd

Distributions differ, so check the defaults before you create accounts:

$ grep CREATE_HOME /etc/login.defs
$ useradd -D | grep SHELL
Option Meaning
-c Comment (full name)
-d Home directory path
-m / -M Create / don’t create the home directory
-s Login shell
-g Primary group (the group must exist)
-G Supplementary groups, comma-separated
-u Specific UID
-e Account expiration date, YYYY-MM-DD
-f Inactive days after password expiration (-1 means never)
-r Create a system account
-k Use a different skeleton directory
-D Show or change the defaults
$ sudo useradd -m -s /bin/bash -c "Jim Kirk" JKirk     # explicit and portable
$ getent passwd JKirk
JKirk:x:1002:1002:Jim Kirk:/home/JKirk:/bin/bash
$ sudo getent shadow JKirk
JKirk:!:17806:0:99999:7:::
$ sudo ls -a /home/JKirk
.  ..  .bash_logout  .bashrc  .profile
  • getent reads account databases (passwd, shadow, group, gshadow) through the system’s name service. It also returns accounts from LDAP or other directories, not just local files. Reading shadow requires sudo.
  • adduser (Debian and Ubuntu) is an interactive, higher-level wrapper configured in /etc/adduser.conf. Those distributions call useradd a “low-level utility.”
  • Don’t set passwords with useradd -p. It expects an already-hashed value and can leave the hash in your shell history. Use passwd instead.

Classroom convention: Always pass -m -s /bin/bash (and -c) when you create interactive accounts with useradd, so the result is the same on every distribution.

5. Passwords and aging: passwd and chage

$ sudo passwd DAdams          # set or reset another user's password
$ passwd                      # change your own password
passwd option Meaning
-l / -u Lock / unlock (adds or removes the leading ! in /etc/shadow)
-d Delete the password
-e Expire the password now (the user must change it at the next login)
-n / -x / -w / -i Minimum days / maximum days / warning days / inactive days
-S Status: P/PS usable, NP no password, L/LK locked, then the aging values
$ sudo passwd -S DAdams
DAdams PS 2018-10-01 0 99999 7 -1 (Password set, SHA512 crypt.)
$ sudo chage -l DAdams
Last password change                                : Oct 02, 2018
Password expires                                    : never
Password inactive                                   : never
Account expires                                     : never
Minimum number of days between password change      : 0
Maximum number of days between password change      : 99999
Number of days of warning before password expires   : 7

chage shows aging information as readable dates. It can change the values interactively (sudo chage DAdams) or with options:

$ sudo chage -m 5 -M 90 -W 14 -I 3 DAdams     # min, max, warn, inactive
$ sudo chage -E 2026-12-18 intern1            # account expires at the end of the term
$ sudo chage -d 0 newhire                     # force a password change at the next login

A 14–15 day warning period covers a two-week vacation.

6. Modifying and deleting accounts

usermod option Meaning
-c Change the comment
-d (with -m) New home directory (-m moves the existing files)
-e Account expiration date
-f Inactive days
-g Change the primary group
-aG Append supplementary groups
-G alone Replace all supplementary groups
-l Rename the login (doesn’t rename the home directory)
-L / -U Lock / unlock
-s Change the shell
-u Change the UID
$ sudo usermod -s /bin/bash DBowman        # fix a wrong default shell
$ sudo usermod -L DAdams                   # lock the account
$ sudo passwd -S DAdams                    # shows LK
$ sudo usermod -U DAdams                   # unlock it

The most important usermod warning: usermod -G wheel alice removes alice from every other supplementary group. Always use -aG to add a group.

Locking isn’t complete protection. usermod -L and passwd -l block password logins only. SSH key logins still work. To disable an account completely, also expire it (usermod -e 1 or chage -E 0) or set its shell to /sbin/nologin.

$ sudo userdel -r DBowman       # delete the account, its home directory, and its mail spool
  • Without -r, the home directory is left behind.
  • A “mail spool not found” message is harmless.
  • Before deleting anything, follow the organization’s retention policy. HR or legal may require you to back up the user’s files first. Afterward, find files the user owned elsewhere with find / -nouser.

7. Groups

/etc/group has four fields:

Project42:x:1042:DAdams,JKirk
  1. Group name
  2. Password placeholder (x means see /etc/gshadow)
  3. GID
  4. Supplementary members, comma-separated. Users whose primary group this is don’t appear in this list.
$ groups DAdams                          # the account's groups
$ id DAdams                              # UID, primary GID, and all groups
$ sudo groupadd -g 1042 Project42        # create a group with a chosen GID
$ sudo usermod -aG Project42 DAdams      # add a member
$ sudo gpasswd -d DAdams Project42       # remove a member
$ sudo groupmod -g 1138 Project42        # change the GID
$ sudo groupmod -n Proj42 Project42      # rename the group
$ sudo groupdel Project42                # delete the group
$ sudo find / -gid 1138 2>/dev/null      # find files still assigned to the old GID
  • When USERGROUPS_ENAB yes is set and no -g is given, useradd creates a user private group with the same name as the user.
  • addgroup is the Debian and Ubuntu wrapper for groupadd.
  • Group passwords are stored in /etc/gshadow (a ! there means none is set). They let non-members join a group with newgrp. Because that means sharing a password, don’t use group passwords. Manage access through membership.
  • A user added to a group has to log out and back in, or use newgrp, before the new membership takes effect.
  • After deleting a group, audit the filesystem for files that still carry its GID.

8. Email: MTA, MDA, and MUA

Linux email follows the Unix approach of small cooperating programs:

Role Job Examples
MTA (mail transfer agent) Receives mail and relays it between servers over SMTP. Hands local mail to an MDA. Postfix, Exim, Sendmail
MDA (mail delivery agent) Puts messages into a local user’s mailbox procmail, the MTA’s local delivery agent, Dovecot LDA
MUA (mail user agent) Lets the user read and send mail. It doesn’t receive mail itself. mail, mutt, Thunderbird

The boundaries blur, since some packages combine the MTA and MDA, or the MDA and MUA.

MTA choices

  • Sendmail. Historically dominant and extremely flexible, but its configuration is complex. It had security problems in the past. Its features (forwarding, aliases, mailing lists) became standard everywhere.
  • Postfix. Written by Wietse Venema with security and simplicity in mind. It’s modular (several small programs) and is the default on many distributions.
  • Exim. Created at the University of Cambridge in 1995. It’s a flexible drop-in replacement for Sendmail with a very different configuration, and it’s the Debian default.

Even when a server doesn’t handle user email, an MTA delivers local system messages, such as cron output and security alerts, to root or an administrator.

Sending and reading local mail with mail

The traditional mail program is sometimes called binmail, after /bin/mail. It’s in the mailx package (Red Hat) or bsd-mailx/mailutils (Debian and Ubuntu).

$ mail -s "Lab status" rich
Hi Rich, lab 7 is ready.
<Ctrl+D>
EOT
$ mail                       # read your mailbox (/var/spool/mail/$USER)
& 1                          # read message 1
& d 1                        # delete message 1
& q                          # quit
  • -s sets the subject; quote it if it has spaces.
  • -c sends copies (Cc) and -b sends blind copies (Bcc). The book prints these as -cc and -bc.
  • -v shows delivery details.
  • -f file reads a different mailbox file.
  • End the message body with Ctrl+D.
  • Postfix may not deliver to usernames containing uppercase letters. Check /var/log/maillog or /var/log/mail.log if messages disappear.

The mail queue

$ mailq                      # list messages waiting to be sent
$ sendmail -bp               # the same thing, in Sendmail-compatible form
$ sudo postqueue -p          # Postfix's own command
$ sudo postsuper -d QUEUEID  # Postfix: delete a stuck message

An empty queue reports “Mail queue is empty.” Queue files are under /var/spool/. Use the MTA’s own tools to remove stuck messages rather than deleting files by hand.

Aliases and forwarding

System-wide aliases (root only):

# /etc/aliases
hostmaster: christine,rich
postmaster: root
root: nick
$ sudo newaliases            # rebuild /etc/aliases.db (sendmail -bi does the same)
$ mail -s "Alias test" hostmaster < /dev/null
  • The format is ALIAS: recipient1,recipient2.
  • The MTA reads the binary /etc/aliases.db, so always run newaliases after editing /etc/aliases, then send a test message.
  • Aliases hide real usernames (for example, publishing hostmaster instead of an employee’s login), catch common misspellings, and send root’s mail to a person.

Per-user forwarding:

$ echo rich > ~/.forward
$ chmod 644 ~/.forward
$ rm ~/.forward                # stop forwarding

Sendmail compatibility

Postfix and Exim provide a Sendmail emulation layer, so the commands sendmail, mailq, sendmail -bp, newaliases, and sendmail -bi work no matter which MTA is installed.

9. The syslog protocol

In the mid-1980s, Eric Allman defined syslog for Sendmail. It became the standard message format for the operating system, applications, and network devices: a timestamp, a facility, a severity, and the message.

Facilities identify what produced the message:

Code Keyword Source
0 kern Kernel
1 user User programs
2 mail Mail system
3 daemon Background services
4 / 10 auth / authpriv Security and authentication (authpriv is for private details)
5 syslog The logger itself
6 lpr Printing
7 / 8 news / uucp Legacy news and UUCP
9 cron Job scheduler
11 / 12 ftp / ntp FTP and NTP
13 / 14 security / console Log audit / log alert
16–23 local0–local7 Available for local use (appliances, custom apps)

Severities, from most to least severe:

Code Keyword Meaning
0 emerg System unusable
1 alert Immediate action needed
2 crit Critical condition
3 err Error
4 warning Warning
5 notice Normal but significant
6 info Informational
7 debug Debugging

Memory aid: a lower number means more severe.

Logging software history

  • sysklogd: the original, with syslogd for system events and klogd for kernel events.
  • syslog-ng: added filtering and remote logging. The book spells it “syslogd-ng.”
  • rsyslog: the fast, standard syslog daemon (rsyslogd) on most distributions.
  • systemd-journald: systemd’s binary journal. It isn’t syslog format, but it collects syslog, kernel, boot, and service messages.

10. rsyslog

Rules

The configuration file is /etc/rsyslog.conf plus /etc/rsyslog.d/*.conf. (The book writes /etc/rsyslogd.conf; the daemon is rsyslogd, but the file is rsyslog.conf.) Each rule has the form:

facility.priority    action
Selector Meaning
kern.crit Kernel messages at crit or more severe (crit, alert, emerg)
kern.=crit Only crit
*.emerg Emergencies from any facility
auth,authpriv.* Several facilities, all priorities
*.info;mail.none;authpriv.none Everything at info or above, except mail and authpriv

Actions:

  • Write to a file (a leading - means don’t sync after every write, which is faster but can lose the last messages in a crash).
  • Pipe to a program.
  • Write to a terminal or the console.
  • Send to a remote host.
  • Send to specific users, or to all logged-in users (:omusrmsg:*).
# Ubuntu (Debian family)
auth,authpriv.*                 /var/log/auth.log
*.*;auth,authpriv.none          -/var/log/syslog
kern.*                          -/var/log/kern.log
mail.*                          -/var/log/mail.log
*.emerg                         :omusrmsg:*
# Red Hat family
*.info;mail.none;authpriv.none;cron.none   /var/log/messages
authpriv.*                                 /var/log/secure
mail.*                                     -/var/log/maillog
cron.*                                     /var/log/cron
local7.*                                   /var/log/boot.log

Where to look first: On Debian and Ubuntu, start with /var/log/syslog and /var/log/auth.log. On Red Hat, start with /var/log/messages and /var/log/secure. Applications often have their own directories, such as /var/log/apache2/ or /var/log/httpd/.

Sending logs to a central log server

A central log host collects logs from many clients, which helps both troubleshooting and security (an attacker who breaks into one server can’t erase the copies elsewhere). On each client, add a rule at the end of /etc/rsyslog.conf:

*.*  @@(z9)loghost.example.edu:6514
  • @ means UDP. @@ means TCP. Use TCP when losing messages matters.
  • (z1)–(z9) is optional zlib compression, from lowest to highest.
  • Host: a fully qualified name or IP address. Put IPv6 addresses in brackets.
  • :port is optional. Syslog traditionally uses port 514.

Then restart the service: sudo systemctl restart rsyslog. Port 6514 is normally used for syslog over TLS, which needs more configuration than this.

Rotating logs with logrotate

logrotate runs daily (from cron or a systemd timer). It renames, compresses, removes, and optionally mails old logs so they don’t fill the disk.

# /etc/logrotate.conf (global settings)
weekly
rotate 4
create
dateext
#compress
include /etc/logrotate.d

/var/log/wtmp {
    monthly
    create 0664 root utmp
    minsize 1M
    rotate 1
}
  • Global settings come first. Per-file blocks override them.
  • include /etc/logrotate.d pulls in per-package files, usually named after the service they manage.
  • Rotated file names: with dateext you get btmp-20260901. Without it you get mail.log.1, mail.log.2, and so on, where the highest number is the oldest.
Directive Meaning
hourly / daily / weekly [n] / monthly Rotation schedule (hourly also needs logrotate to run hourly)
size n Rotate by size instead of time (add k, M, or G for units)
rotate n Keep n old copies (0 deletes instead of keeping)
compress gzip rotated files
missingok Don’t complain if the log is missing
notifempty Skip empty logs
create mode owner group Create a new empty log with these attributes
copytruncate Copy and then truncate, for programs that keep the file open
dateext / dateformat Date-based suffix and its format

The status file is /var/lib/logrotate/logrotate.status (Red Hat) or /var/lib/logrotate/status (Debian). Test a configuration with sudo logrotate -d /etc/logrotate.conf (a dry run).

/var/log/btmp records failed logins, which you read with lastb. /var/log/wtmp records logins, which you read with last.

Writing and reading log entries

$ logger "Backup script finished"
$ logger -p local0.warning -t backup "Disk 90% full"
$ tail -f /var/log/syslog              # follow new entries (Ctrl+C to stop)
$ grep -i "failed password" /var/log/auth.log
logger option Meaning
-p Facility and priority
-t Tag, which makes entries easy to search for
-i Include the process ID
-f Log the contents of a file
-s Also write to standard error

rsyslog adds the timestamp, host name, and user to every entry.

11. systemd-journald

The journal stores entries in an indexed binary format. You read it only with journalctl. It captures syslog messages, kernel messages, boot messages, and service output, along with metadata such as unit, PID, and UID.

Configuration: /etc/systemd/journald.conf

Directive Meaning
Storage= auto (default), persistent, volatile, or none
Compress= Compress journal objects (default yes)
ForwardToSyslog= Also pass messages to a syslog daemon
ForwardToWall= Send emergency messages to all terminals
SystemMaxUse= / SystemKeepFree= / SystemMaxFileSize= Size limits for persistent storage (/var/log/journal)
RuntimeMaxUse= / RuntimeKeepFree= / RuntimeMaxFileSize= The same limits for volatile storage (/run/log/journal)
MaxFileSec= Maximum time before a journal file is rotated

Storage modes:

  • auto writes to /var/log/journal if that directory exists; otherwise it uses /run/log/journal, which is lost at reboot.
  • persistent creates /var/log/journal if needed and writes there.
  • volatile keeps the journal in memory only (/run/log/journal).
  • none discards messages.

Making the journal persistent

$ sudo sed -i 's/^#\?Storage=.*/Storage=persistent/' /etc/systemd/journald.conf
$ sudo systemctl restart systemd-journald      # reload doesn't work for journald
$ ls /var/log/journal/

Setting Storage=persistent and restarting lets journald create the directory with the correct permissions. That’s safer than creating it by hand.

Journal files

  • The active files are system.journal and, if configured, user-UID.journal for each user. They live in a directory named after the machine ID.
  • Rotated (archived) files have @ and a long ID in their names.
  • sudo journalctl --rotate forces a rotation.

Viewing and filtering

The syntax is journalctl [OPTIONS] [MATCHES]. Reading the full system journal requires root or membership in the systemd-journal group (on some distributions, adm or wheel).

Option Meaning
-n N Last N entries
-e Jump to the end
-r Newest first
-f Follow new entries, like tail -f
-k Kernel messages (like dmesg)
-b / -b -1 This boot / the previous boot
-u unit Only entries from one unit
-p err This priority and more severe
-S / -U Since / until ("2026-09-24 08:00", today, yesterday, -1h)
--no-pager Don’t use less
-o verbose / -o json Show every field / JSON output
$ sudo journalctl -u ssh --since today
$ sudo journalctl -p err -b
$ sudo journalctl _UID=1001 --since "2026-09-24 08:00"
$ sudo journalctl _SYSTEMD_UNIT=sshd.service + _SYSTEMD_UNIT=cron.service
$ sudo journalctl -f

Field matches use the form FIELD=value. Examples include _SYSTEMD_UNIT=, _PID=, _UID=, _HOSTNAME=, PRIORITY=, _TRANSPORT=, and _UDEV_SYSNAME=. Matches on different fields are combined with AND. Separate them with + to get OR. The full list is in man systemd.journal-fields.

Maintaining the journal

$ journalctl --disk-usage
$ sudo journalctl --vacuum-size=300M     # remove archived files down to 300 MB
$ sudo journalctl --vacuum-time=2weeks   # remove archived entries older than 2 weeks
$ sudo journalctl --sync                 # flush to disk before copying journal files

The vacuum options affect archived files only, so the reported usage can stay above the target you set.

Journals from other systems

  • journalctl -D /path/to/dir reads journal files in another directory, such as a rescued disk.
  • --file=pattern reads specific files.
  • -m (--merge) combines entries from all available journals in one view, for example on a central host that receives journals through systemd-journal-remote.

Writing journal entries and combining with rsyslog

$ echo "Nightly job done" | systemd-cat -t nightly -p info
$ logger "Test of logger"         # also reaches the journal on systemd systems
$ journalctl -t nightly

Most distributions run journald and rsyslog together:

  • Journal client (preferred): rsyslog reads the journal with the imjournal module (or the imuxsock socket), so no boot messages are lost.
  • Forward to syslog: set ForwardToSyslog=yes so journald passes messages through /run/systemd/journal/syslog.

12. Keeping time: clocks and standards

Accurate time matters for scheduled jobs, logs you need to compare across servers, and authentication systems such as Kerberos and TLS certificate checks.

  • Local time (“wall clock” time) depends on the time zone.
  • UTC (Coordinated Universal Time) is the same everywhere. Use it for servers, logs, and meetings across time zones.
  • Hardware clock (RTC, real-time clock). Runs on the motherboard battery (CMOS battery) while the system is off. Keep it in UTC to avoid daylight saving time problems.
  • Software (system) clock. Set from the RTC at boot and used by the operating system. It drifts, especially on busy systems, which is why NTP exists.

hwclock

$ sudo hwclock -r            # --show: read the RTC (requires root)
$ sudo hwclock -w            # --systohc: system time → RTC
$ sudo hwclock -s            # --hctosys: RTC → system time
$ sudo hwclock -u -w         # --utc: keep the RTC in UTC

date and timedatectl

$ date
$ sudo date 05301430                        # MMDDhhmm (local time)
$ sudo date -u 05301930                     # the same, given in UTC
$ timedatectl                               # summary: local, UTC, RTC, zone, NTP
$ sudo timedatectl set-ntp false            # required before setting the time by hand
$ sudo timedatectl set-time "2026-09-24 16:15:00"
$ sudo timedatectl set-ntp true
$ sudo timedatectl set-local-rtc 0          # RTC in UTC (1 = local time; avoid)
  • set-time fails with “Automatic time synchronization is enabled” until you turn off NTP. The book uses set-ntp 0 and set-ntp 1; false and true are clearer.
  • Classroom convention: use timedatectl on systemd systems, and use hwclock and date when you need to know the individual pieces.
  • If you mess up the clock during a lab, turn NTP back on (or reboot).

13. Network Time Protocol (NTP)

NTP synchronizes clocks over the network using UDP port 123. The same software can act as a client, a server, or a peer.

The stratum hierarchy

  • Stratum 0: reference clocks, such as atomic clocks and GPS receivers.
  • Stratum 1: servers connected directly to a stratum 0 device.
  • Stratum 2 and below: each level gets its time over the network from the level above, so accuracy drops slightly at each step.
  • Stratum 16: unsynchronized.

NTP software measures network delay and compensates for it.

SNTP (Simple NTP) is a lightweight version for devices that don’t need high accuracy. systemd-timesyncd is an SNTP client.

Choosing time sources

  • Your ISP or organization’s time servers are the most accurate and cause the least network traffic.
  • pool.ntp.org is a volunteer pool. Each request gets a different member. It has regional and vendor subsets such as us.pool.ntp.org, north-america.pool.ntp.org, and 2.ubuntu.pool.ntp.org. Number the entries 0., 1., 2., and so on to get different servers.
  • Google’s leap-smearing servers (time1–time4.google.com) spread a leap second across a whole day instead of inserting it at once. Don’t mix smeared and non-smeared sources.
  • Published server lists are also available. Follow each server’s usage rules.
  • For high-precision needs, use local hardware reference clocks rather than servers on the internet.

ntpd (the traditional daemon)

  • Package ntp, daemon ntpd, configuration /etc/ntp.conf.
server 0.us.pool.ntp.org iburst
server 1.us.pool.ntp.org iburst
server 2.us.pool.ntp.org iburst

iburst sends a quick burst of requests at startup to speed up the first synchronization.

$ sudo ntpdate 0.pool.ntp.org       # one-time manual correction (before starting ntpd)
$ sudo systemctl enable --now ntpd
$ ntpstat                           # synchronized? to what, at which stratum, how accurate
$ ntpq -p                           # peer table
  • Insane time: older ntpd wouldn’t correct a clock that was off by more than about 17 minutes, which is why administrators ran ntpdate first.
  • Reading ntpq -p: * marks the source currently in use, + marks good candidates, st is the stratum, and reach is an octal record of recent responses (377 means the last 8 all succeeded). Delay, offset, and jitter are in milliseconds.
  • After NTP has corrected the system clock, copy it to the hardware clock with hwclock --systohc.

chrony (the modern default)

chronyd synchronizes faster and handles busy networks, virtual machines, and machines that are often turned off or disconnected. It’s the default on Red Hat, Fedora, and SUSE, and it’s commonly used on Ubuntu too.

  • Package chrony, daemon chronyd (the service is chrony on Debian and Ubuntu).
  • Configuration is /etc/chrony.conf or /etc/chrony/chrony.conf.
pool 2.ubuntu.pool.ntp.org iburst maxsources 2
server time.example.edu iburst
rtcsync
  • server is one host. pool is a pool name that resolves to several servers. maxsources limits how many servers chrony uses from a pool, and it replaces any that fail.
  • rtcsync makes chrony copy the system time to the RTC regularly, so you don’t need to run hwclock --systohc.
  • Open UDP port 123 in the firewall if the machine serves time to others. Restart chronyd after editing the configuration.
$ sudo systemctl enable --now chronyd
$ chronyc sources -v            # like ntpq -p, with a legend (^* = current source)
$ chronyc sourcestats           # offset and drift statistics for each source
$ chronyc tracking              # how far off the system clock is, stratum, leap status

Only one time daemon should run at a time. Check which one is active with systemctl status chronyd ntpd systemd-timesyncd.

Classroom application

  1. Account life cycle. Check CREATE_HOME and useradd -D on two distributions. Create an intern account with a home directory, the Bash shell, a comment, and an expiration date at the end of the semester. Set its password, force a change at first login, then lock, unlock, and finally delete it with userdel -r. Explain each change you see in /etc/passwd, /etc/shadow, and chage -l.
  2. Group trap. Add a user to two groups with -aG, then run usermod -G without -a and compare the output of id before and after.
  3. Local mail. Send mail between two accounts, add a /etc/aliases entry and run newaliases, then test a .forward file.
  4. rsyslog rule. Add local0.* /var/log/lab.log, restart rsyslog, send a message with logger -p local0.notice, and find it in the file and in journalctl -t.
  5. Journal detective. Make the journal persistent, reboot, and then use -b -1, -u, -p err, and --since to answer specific questions (for example, “When did sshd last restart?”).
  6. Time audit. Report the timedatectl output, the active time daemon, and chronyc tracking. Add a pool with maxsources and confirm the change in chronyc sources -v.

Common misconceptions

  • “/etc/passwd holds passwords.” It holds an x placeholder. The hashes are in /etc/shadow, which only root can read.
  • “usermod -G adds a group.” Without -a, it replaces all of the user’s supplementary groups.
  • “A locked account can’t log in at all.” -L and -l block passwords only. SSH keys still work unless the account is expired or its shell is nologin.
  • “Password expiration and account expiration are the same.” Password expiration has a grace period. Account expiration doesn’t.
  • “Editing /etc/aliases is enough.” You must also run newaliases.
  • “kern.crit logs only critical messages.” It logs crit and more severe. Use kern.=crit for exactly crit.
  • “@ sends logs over TCP.” @ is UDP and @@ is TCP.
  • “Vacuuming shrinks the whole journal.” It removes archived files only. Active files are untouched.
  • “The hardware clock should be set to local time.” Keep it in UTC and let the time zone handle local time.
  • “Run ntpd and chrony together for redundancy.” Run only one time daemon, with several sources.

Lesson summary

  • Accounts: /etc/login.defs, /etc/default/useradd, and /etc/skel shape what useradd does. /etc/passwd, /etc/shadow, /etc/group, and /etc/gshadow store the results. Use passwd and chage for passwords and aging, usermod -aG for group changes, and userdel -r (following the retention policy) to remove accounts.
  • Email: MTAs (Postfix, Exim, Sendmail) move mail, MDAs deliver it, and MUAs read it. Use mail, mailq/sendmail -bp, /etc/aliases plus newaliases, and ~/.forward.
  • Logging: syslog uses facility, severity, and message. rsyslog rules use facility.priority action, send to remote hosts with @/@@, rotate with logrotate, and accept entries from logger.
  • Journal: journald stores binary, indexed entries. Configure it in journald.conf, make it persistent, search it with journalctl options and field matches, clean it up with vacuum options, and write to it with systemd-cat.
  • Time: keep the RTC in UTC. Use hwclock, date, and timedatectl for manual work, and NTP (preferably chrony, with pools, iburst, and rtcsync) to keep clocks accurate.

The next lesson is Configuring Basic Networking.