阅读视图

发现新文章,点击刷新页面。

Git Cheatsheet

Getting Started

Initialize and configure Git.

Command Description
git init Initialize a new repository
git clone url Clone a remote repository
git status Show working tree status
git config --global user.name "Name" Set global name
git config --global user.email "email" Set global email

Staging and Committing

Add changes and create commits.

Command Description
git add file Stage a file
git add . Stage all changes
git commit -m "message" Commit staged changes
git commit --amend Amend last commit
git revert commit Revert a commit

Branches

Create, list, and switch branches.

Command Description
git branch List branches
git branch name Create a branch
git switch name Switch branch
git switch -c name Create and switch
git branch -d name Delete local branch
git branch -D name Force delete branch
git branch -m new-name Rename current branch

Merge and Rebase

Combine branches and rewrite history.

Command Description
git merge branch Merge branch into current
git merge --no-ff branch Merge with merge commit
git merge --abort Abort merge in progress
git rebase branch Rebase onto branch
git rebase -i HEAD~3 Interactive rebase last 3
git rebase --abort Abort rebase in progress
git cherry-pick commit Apply a single commit

Remote Repositories

Work with remotes and synchronization.

Command Description
git remote -v List remotes
git remote add origin url Add remote
git remote set-url origin url Change remote URL
git remote remove origin Remove remote
git fetch Fetch from remote
git pull Fetch and merge
git push Push to remote
git push -u origin branch Push and set upstream
git push origin --delete branch Delete remote branch

Stash

Temporarily save uncommitted changes.

Command Description
git stash Stash changes
git stash push -m "message" Stash with message
git stash list List stashes
git stash pop Apply and remove latest
git stash apply stash@{0} Apply without removing
git stash drop stash@{0} Delete a stash
git stash clear Delete all stashes

History and Diff

Inspect history and changes.

Command Description
git log View commit history
git log --oneline --graph Compact graph view
git log -p file History of a file
git show commit Show a commit
git blame file Show who changed each line
git diff Unstaged diff
git diff --staged Staged diff
git diff branch1..branch2 Compare branches

Undo and Cleanup

Discard or reset changes safely.

Command Description
git restore file Discard local changes
git restore --staged file Unstage a file
git reset --soft HEAD~1 Undo commit, keep changes
git reset --hard HEAD Reset to last commit
git clean -fd Remove untracked files/dirs

Tags

Create and manage tags.

Command Description
git tag List tags
git tag v1.0.0 Create tag
git tag -a v1.0.0 -m "msg" Annotated tag
git push --tags Push all tags
git push origin :refs/tags/v1.0.0 Delete remote tag

.gitignore

Exclude files from version control.

Pattern Description
*.log Ignore all .log files
node_modules/ Ignore directory
!important.log Negate ignore rule
**/build Ignore in any directory
git rm --cached file Untrack a tracked file

Iptables Cheatsheet

View Rules

Inspect current firewall rules.

Command Description
sudo iptables -L List rules
sudo iptables -L -n List without resolving names
sudo iptables -L -v Verbose output
sudo iptables -L -n --line-numbers Show rule numbers
sudo iptables -S Show rules as commands
sudo iptables -t nat -L -n -v View NAT rules

Default Policies

Set default policies for chains.

Command Description
sudo iptables -P INPUT DROP Default drop inbound
sudo iptables -P FORWARD DROP Default drop forwarding
sudo iptables -P OUTPUT ACCEPT Default allow outbound

Allow Traffic

Allow common inbound traffic.

Command Description
sudo iptables -A INPUT -i lo -j ACCEPT Allow loopback
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT Allow established
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT Allow SSH
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT Allow HTTP
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT Allow HTTPS
sudo iptables -A INPUT -p icmp -j ACCEPT Allow ping
sudo iptables -A INPUT -s 192.168.1.0/24 -j ACCEPT Allow subnet

Block Traffic

Drop or reject traffic.

Command Description
sudo iptables -A INPUT -s 203.0.113.10 -j DROP Drop IP address
sudo iptables -A INPUT -s 203.0.113.0/24 -j DROP Drop subnet
sudo iptables -A INPUT -p tcp --dport 23 -j DROP Block Telnet
sudo iptables -A INPUT -p tcp --dport 25 -j REJECT Reject SMTP
sudo iptables -A INPUT -m mac --mac-source XX:XX:XX:XX:XX:XX -j DROP Block MAC address

Port Forwarding (DNAT)

Redirect traffic to a different host or port.

Command Description
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.1.10:80 Forward port to host
sudo iptables -t nat -A PREROUTING -p tcp --dport 8080 -j REDIRECT --to-port 80 Redirect local port
sudo iptables -A FORWARD -p tcp -d 192.168.1.10 --dport 80 -j ACCEPT Allow forwarded traffic

NAT (Masquerade)

Enable NAT for outbound traffic.

Command Description
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE NAT for interface
sudo iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j SNAT --to-source 203.0.113.1 Static NAT
sudo sysctl -w net.ipv4.ip_forward=1 Enable IP forwarding

Rate Limiting

Limit connection rates to prevent abuse.

Command Description
sudo iptables -A INPUT -p tcp --dport 22 -m limit --limit 3/min --limit-burst 3 -j ACCEPT Limit SSH attempts
sudo iptables -A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 50 -j DROP Limit connections per IP
sudo iptables -A INPUT -p icmp -m limit --limit 1/sec -j ACCEPT Limit ping rate

Logging

Log matched packets for debugging.

Command Description
sudo iptables -A INPUT -j LOG --log-prefix "IPT-DROP: " Log dropped packets
sudo iptables -A INPUT -p tcp --dport 22 -j LOG --log-prefix "SSH: " --log-level 4 Log SSH access
sudo iptables -A INPUT -m limit --limit 5/min -j LOG Log with rate limit

Delete and Insert Rules

Manage rule order and removal.

Command Description
sudo iptables -D INPUT 3 Delete rule number 3
sudo iptables -D INPUT -p tcp --dport 80 -j ACCEPT Delete by specification
sudo iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT Insert rule at top
sudo iptables -R INPUT 3 -p tcp --dport 443 -j ACCEPT Replace rule number 3
sudo iptables -F Flush all rules
sudo iptables -F INPUT Flush INPUT chain only

Save and Restore

Persist rules between reboots.

Command Description
sudo iptables-save > /etc/iptables/rules.v4 Save rules
sudo iptables-restore < /etc/iptables/rules.v4 Restore rules
sudo apt install iptables-persistent Auto-persist on Debian/Ubuntu
sudo service iptables save Save on RHEL/CentOS

Editorial Policy

Linuxize publishes practical Linux tutorials and guides for system administrators, DevOps engineers, and developers. This policy explains how we research, test, update, and correct our content.

Who Writes Our Content

Linuxize was founded by Dejan Panovski , an RHCSA-certified Linux professional with over 20 years of experience as a system administrator, DevOps engineer, and technical writer. The vast majority of articles on this site are written and maintained by Dejan. Guest contributors follow the same editorial standards outlined below.

Editorial Standards

  • Accuracy first — We verify commands, outputs, and configuration examples before publication.
  • Practical focus — Articles are written for real systems and real use cases, not theoretical examples.
  • Clear steps — We present instructions in a logical order with explanations before code blocks.
  • Security awareness — We highlight risky commands and recommend safe defaults.

Testing and Verification

We test commands and workflows on real systems whenever possible. For distro-specific posts, we verify instructions on the named distribution and version. If a procedure changes across versions, we note the difference or provide separate versions.

Updates and Maintenance

We regularly review and update published articles to keep them accurate and current. Updated articles display a “Last updated” date to indicate when they were last revised. Common reasons for updates include:

  • Software version changes or deprecated commands
  • Improved procedures or better practices
  • Reader feedback and error reports
  • New sections such as FAQ, Quick Reference, or Troubleshooting

Corrections

If you find an error, outdated step, or missing detail, please contact us at hello@linuxize.com . We review corrections promptly and update the article when needed.

Contributor Guidelines

Guest submissions follow the same standards for accuracy, testing, and clarity. All submissions are reviewed for technical correctness and editorial consistency before publication.

Transparency

We do not accept sponsored content that compromises the integrity of our tutorials. Advertising is kept separate from editorial decisions.

How to Set or Change Timezone on Debian 13

A time zone is a geographic region that has the same standard time. Using the correct time zone is essential for many system tasks and processes. For example, the cron daemon uses the system’s time zone for executing cron jobs, and the timestamps in log files are based on the same time zone.

On Debian 13 (Trixie), the system’s time zone is set during the installation, but it can be easily changed at a later time.

This article explains how to set or change the time zone on Debian 13.

Quick Reference

Task Command
Check current time zone timedatectl
List all time zones timedatectl list-timezones
Filter time zones timedatectl list-timezones | grep -i "keyword"
Set time zone sudo timedatectl set-timezone Region/City
Set to UTC sudo timedatectl set-timezone Etc/UTC
Interactive method sudo dpkg-reconfigure tzdata
Verify symlink ls -l /etc/localtime

Checking the Current Time Zone

timedatectl is a command-line utility that allows you to view and change the system’s time and date. It is available on all modern systemd-based Linux systems, including Debian 13.

To view the current time zone, run the timedatectl command without any options:

Terminal
timedatectl

The output below shows that the system’s time zone is set to UTC:

output
 Local time: Sat 2026-02-07 14:30:44 UTC
Universal time: Sat 2026-02-07 14:30:44 UTC
RTC time: Sat 2026-02-07 14:30:44
Time zone: Etc/UTC (UTC, +0000)
System clock synchronized: yes
systemd-timesyncd.service active: yes
RTC in local TZ: no

The system time zone is configured by symlinking the /etc/localtime file to a binary time zone identifier in the /usr/share/zoneinfo directory. You can also check the time zone by viewing the path the symlink points to using the ls command:

Terminal
ls -l /etc/localtime
output
lrwxrwxrwx 1 root root 27 Feb 7 14:30 /etc/localtime -> /usr/share/zoneinfo/Etc/UTC

Changing the Time Zone on Debian 13

Before changing the time zone, you need to find the long name of the time zone you want to use. Time zones use a “Region/City” format.

To list all available time zones, use the timedatectl command with the list-timezones option:

Terminal
timedatectl list-timezones
output
...
America/Montserrat
America/Nassau
America/New_York
America/Nipigon
America/Nome
...

To filter the list for a specific region or city, pipe the output through grep:

Terminal
timedatectl list-timezones | grep -i "europe"

Once you identify the correct time zone for your location, run the following command as root or user with sudo privileges :

Terminal
sudo timedatectl set-timezone <your_time_zone>

For example, to change the system’s time zone to Europe/London, you would run:

Terminal
sudo timedatectl set-timezone Europe/London

Verify the change by invoking the timedatectl command again:

Terminal
timedatectl
output
 Local time: Sat 2026-02-07 14:35:09 GMT
Universal time: Sat 2026-02-07 14:35:09 UTC
RTC time: Sat 2026-02-07 14:35:09
Time zone: Europe/London (GMT, +0000)
System clock synchronized: yes
systemd-timesyncd.service active: yes
RTC in local TZ: no

The time zone has been successfully changed.

Using dpkg-reconfigure

On Debian systems, you can also change the time zone using the tzdata package. This method provides an interactive text-based interface:

Terminal
sudo dpkg-reconfigure tzdata

The command opens a menu where you can select the geographic area and then the city or region. After making your selection, the system time zone is updated automatically.

Troubleshooting

Changes do not persist after reboot
Ensure /etc/localtime is a symlink to a valid file in /usr/share/zoneinfo and re-run sudo timedatectl set-timezone Region/City.

Clock is still wrong after changing the time zone
Check that NTP sync is active with timedatectl status. If it is disabled, enable time synchronization: sudo timedatectl set-ntp true.

FAQ

How do I check the current time zone on Debian 13?
Run the timedatectl command without any arguments. The “Time zone” line in the output shows the currently configured time zone.

Can I change the time zone without restarting?
Yes. The timedatectl set-timezone command takes effect immediately. There is no need to restart the system or any services.

What is the difference between timedatectl and dpkg-reconfigure tzdata?
Both methods achieve the same result. timedatectl is a single command that works on any systemd-based distribution. dpkg-reconfigure tzdata provides an interactive menu and is specific to Debian and Ubuntu systems.

Where are the time zone files stored?
Time zone data files are stored in the /usr/share/zoneinfo directory. The /etc/localtime file is a symlink that points to the active time zone file in that directory.

Conclusion

To change the time zone on Debian 13, use the sudo timedatectl set-timezone command followed by the long name of the time zone you want to set.

If you have any questions, feel free to leave a comment below.

Awk Cheatsheet

Basic Usage

Process lines and fields.

Command Description
awk '{print}' file.txt Print all lines
awk '{print $1}' file.txt Print first field
awk '{print $1, $3}' file.txt Print multiple fields
awk 'NR==1{print}' file.txt Print first line
awk 'NR>1{print}' file.txt Skip header

Field Separators

Change the input field separator.

Command Description
awk -F ':' '{print $1}' /etc/passwd Use colon separator
awk -F ',' '{print $2}' file.csv CSV column
awk -F '\t' '{print $1}' file.tsv TSV column
`awk ‘BEGIN{FS=" “} {print $2}’ file.txt`

Pattern Matching

Filter lines by conditions.

Command Description
awk '/error/ {print}' file.log Match regex
awk '$3 > 100 {print}' file.txt Numeric condition
awk '$1 == "root" {print}' /etc/passwd String match
awk 'NF == 3 {print}' file.txt Exact field count
awk 'NF > 0 {print}' file.txt Non-empty lines

Calculations

Do arithmetic and totals.

Command Description
awk '{sum += $2} END {print sum}' file.txt Sum column
awk '{sum += $2} END {print sum/NR}' file.txt Average column
awk 'BEGIN{print 5*7}' Simple calculation
awk '$2 > 0 {print $1, $2*1.2}' file.txt Multiply column

Output Formatting

Format output and columns.

Command Description
awk '{printf "%s\t%s\n", $1, $2}' file.txt Tab-separated output
awk '{printf "%-20s %s\n", $1, $2}' file.txt Left-align columns
awk '{printf "%.2f\n", $1}' file.txt Format numbers
awk 'BEGIN{OFS=","} {print $1,$2}' file.txt Set output separator

Common Options

Useful flags to remember.

Option Description
-F Set input field separator
-v Set variable (e.g., -v limit=10)
-f Read program from file
-E Use extended regex (gawk)
--posix POSIX mode

Grep Cheatsheet

Basic Search

Find matching lines in a file.

Command Description
grep "pattern" file.txt Search a single file
grep -i "pattern" file.txt Case-insensitive search
grep -n "pattern" file.txt Show line numbers
grep -v "pattern" file.txt Invert match
grep -w "word" file.txt Match whole words

Multiple Files

Search across multiple files.

Command Description
grep "pattern" file1 file2 Search specific files
grep "pattern" *.log Search by glob
grep -l "pattern" *.log Show matching filenames
grep -L "pattern" *.log Show non-matching filenames

Recursive Search

Search directories recursively.

Command Description
grep -r "pattern" dir/ Recursive search
grep -R "pattern" dir/ Follow symlinks
grep -r --include="*.conf" "pattern" dir/ Include file types
grep -r --exclude="*.log" "pattern" dir/ Exclude file types

Context Output

Show lines around matches.

Command Description
grep -C 3 "pattern" file.txt 3 lines before and after
grep -A 2 "pattern" file.txt 2 lines after
grep -B 2 "pattern" file.txt 2 lines before

Count and Only Matches

Summarize or extract matches.

Command Description
grep -c "pattern" file.txt Count matching lines
grep -o "pattern" file.txt Only the matching part
grep -m 1 "pattern" file.txt Stop after 1 match

Extended Regex

Use more powerful patterns.

Command Description
[`grep -E “foo bar” file.txt`](/post/grep-multiple-patterns/)
grep -E "colou?r" file.txt Optional character
grep -E "[0-9]{3}" file.txt Repetition
grep -E "^start" file.txt Line starts with
grep -E "end$" file.txt Line ends with

Common Options

Useful flags to remember.

Option Description
-i Ignore case
-n Show line numbers
-v Invert match
-w Match whole words
-r Recursive search
-E Extended regex
-F Fixed strings (no regex)
-H Always show filename
-q Quiet mode (exit status only)

Traceroute Command in Linux

The traceroute command is a network diagnostic tool that displays the path packets take from your system to a destination host. It shows each hop (router) along the route and the time it takes for packets to reach each one.

Network administrators use traceroute to identify where packets are being delayed or dropped, making it essential for troubleshooting connectivity issues, latency problems, and routing failures.

This guide covers how to use the traceroute command with practical examples and explanations of the most common options.

Syntax

The general syntax for the traceroute command is:

Terminal
traceroute [OPTIONS] DESTINATION
  • OPTIONS — Flags that modify the behavior of the command.
  • DESTINATION — The target hostname or IP address to trace.

Installing traceroute

The traceroute command is not installed by default on all Linux distributions. To check if it is available on your system, type:

Terminal
traceroute --version

If traceroute is not present, the command will print “traceroute: command not found”. You can install it using your distribution’s package manager.

Install traceroute on Ubuntu and Debian

Terminal
sudo apt update && sudo apt install traceroute

Install traceroute on CentOS and Fedora

Terminal
sudo dnf install traceroute

On older CentOS versions, use sudo yum install traceroute.

Install traceroute on Arch Linux

Terminal
sudo pacman -S traceroute

How traceroute works

When you run traceroute, it sends packets with incrementally increasing TTL (Time to Live) values, starting at 1. Each router along the path decrements the TTL by 1. When the TTL reaches 0, the router discards the packet and sends back an ICMP “Time Exceeded” message.

By increasing the TTL with each round of packets, traceroute discovers each hop along the route until the packets reach the final destination.

By default, traceroute sends three UDP packets per hop (on Linux) and displays the round-trip time for each packet.

Basic Usage

To trace the route to a destination, run traceroute followed by the hostname or IP address:

Terminal
traceroute google.com

The output should look something like this:

output
traceroute to google.com (142.250.185.78), 30 hops max, 60 byte packets
1 router.local (192.168.1.1) 1.234 ms 1.102 ms 1.056 ms
2 10.0.0.1 (10.0.0.1) 12.345 ms 12.234 ms 12.123 ms
3 isp-gateway.example.net (203.0.113.1) 15.678 ms 15.567 ms 15.456 ms
4 core-router.example.net (198.51.100.1) 20.123 ms 20.012 ms 19.901 ms
5 google-peer.example.net (192.0.2.1) 22.345 ms 22.234 ms 22.123 ms
6 142.250.185.78 (142.250.185.78) 25.678 ms 25.567 ms 25.456 ms

Understanding the Output

Each line in the traceroute output represents a hop along the route. Let us break down what each field means:

  • Hop number — The sequential number of the router in the path (1, 2, 3, etc.).
  • Hostname — The DNS name of the router, if available.
  • IP address — The IP address of the router in parentheses.
  • Round-trip times — Three time measurements in milliseconds, one for each probe packet sent to that hop.

The first line shows the destination, maximum number of hops (default 30), and packet size (default 60 bytes).

Interpreting the Results

Asterisks (* * *) indicate that no response was received for that hop. This can happen when:

  • The router is configured to not respond to traceroute probes.
  • A firewall is blocking the packets.
  • The packets were lost due to network congestion.

Increasing latency at a specific hop suggests a bottleneck or congested link at that point in the network.

Consistent high latency from a certain hop onward indicates the issue is at or before that router.

Common Options

The traceroute command accepts several options to customize its behavior:

  • -n — Do not resolve IP addresses to hostnames. This speeds up the output by skipping DNS lookups.
  • -m max_ttl — Set the maximum number of hops (default is 30).
  • -q nqueries — Set the number of probe packets per hop (default is 3).
  • -w waittime — Set the time in seconds to wait for a response (default is 5).
  • -I — Use ICMP ECHO packets instead of UDP (requires root privileges).
  • -T — Use TCP SYN packets instead of UDP (requires root privileges).
  • -p port — Set the destination port for UDP or TCP probes.
  • -s source_addr — Use the specified source IP address.
  • -i interface — Send packets through the specified network interface.

Skip DNS Resolution

To speed up the trace and display only IP addresses, use the -n option:

Terminal
traceroute -n google.com
output
traceroute to google.com (142.250.185.78), 30 hops max, 60 byte packets
1 192.168.1.1 1.234 ms 1.102 ms 1.056 ms
2 10.0.0.1 12.345 ms 12.234 ms 12.123 ms
3 203.0.113.1 15.678 ms 15.567 ms 15.456 ms

This is useful when DNS resolution is slow or when you only need IP addresses.

Change Maximum Hops

By default, traceroute stops after 30 hops. To change this limit, use the -m option:

Terminal
traceroute -m 15 google.com

This limits the trace to 15 hops maximum.

Change Number of Probes

To send a different number of probe packets per hop, use the -q option:

Terminal
traceroute -q 1 google.com

This sends only one probe per hop, resulting in faster but less detailed output.

Use ICMP Instead of UDP

By default, Linux traceroute uses UDP packets. Some networks block UDP, so you can use ICMP ECHO packets instead:

Terminal
sudo traceroute -I google.com
Info
The -I option requires root privileges because sending raw ICMP packets requires elevated permissions.

Use TCP Instead of UDP

For networks that block both UDP and ICMP, you can use TCP SYN packets:

Terminal
sudo traceroute -T google.com

You can also specify a port, such as port 443 for HTTPS:

Terminal
sudo traceroute -T -p 443 google.com

This is useful for tracing routes through firewalls that only allow specific TCP ports.

Trace IPv6 Routes

To trace IPv6 routes, use the -6 option:

Terminal
traceroute -6 ipv6.google.com

Specify Source Interface

If your system has multiple network interfaces, you can specify which one to use:

Terminal
traceroute -i eth0 google.com

Or specify the source IP address:

Terminal
traceroute -s 192.168.1.100 google.com

Traceroute vs tracepath

Linux systems often include tracepath, which is similar to traceroute but does not require root privileges and automatically discovers the MTU (Maximum Transmission Unit) along the path.

Feature traceroute tracepath
Root required Yes (for ICMP/TCP) No
Protocol UDP, ICMP, TCP UDP only
MTU discovery No Yes
Customization Many options Limited

Use tracepath for quick traces without root access:

Terminal
tracepath google.com

Use traceroute when you need more control over the probe method or when tracepath does not provide enough information.

Practical Examples

Diagnose Slow Connections

If a website is loading slowly, trace the route to identify where the delay occurs:

Terminal
traceroute -n example.com

Look for hops with significantly higher latency than the previous ones. The hop before the latency spike is often the source of the problem.

Check if a Host is Reachable

If ping shows packet loss, use traceroute to find where packets are being dropped:

Terminal
traceroute google.com

Hops showing * * * followed by successful hops indicate a router that does not respond to probes but forwards traffic. If all remaining hops show * * *, the issue is at or after the last responding hop.

Trace Through a Firewall

If standard UDP probes are blocked, try ICMP or TCP:

Terminal
sudo traceroute -I google.com
sudo traceroute -T -p 80 google.com

Compare Routes to Different Servers

To understand routing differences, trace routes to multiple servers:

Terminal
traceroute -n server1.example.com
traceroute -n server2.example.com

This helps identify whether traffic to different destinations takes different paths through your network.

Quick Reference

Task Command
Basic trace traceroute example.com
Skip DNS resolution traceroute -n example.com
Limit to N hops traceroute -m 15 example.com
One probe per hop traceroute -q 1 example.com
Use ICMP sudo traceroute -I example.com
Use TCP sudo traceroute -T example.com
Use TCP on port 443 sudo traceroute -T -p 443 example.com
Specify interface traceroute -i eth0 example.com
Set timeout traceroute -w 3 example.com
Trace with tracepath tracepath example.com

Troubleshooting

All hops show * * *
The destination or your network may be blocking traceroute probes. Try using ICMP (-I) or TCP (-T) instead of the default UDP. If the issue persists, a firewall between you and the destination is likely blocking all probe types.

Only the first hop responds
Your local router responds, but nothing beyond it does. This often indicates a firewall or routing issue at your ISP. Contact your network administrator or ISP for assistance.

Trace never completes
The destination may not be reachable, or the maximum hop count is too low. Increase the maximum hops with -m 60 and check if the trace progresses further.

High latency at a specific hop
A single hop with high latency does not always indicate a problem. Routers often deprioritize ICMP responses. If the final destination has acceptable latency, the intermediate high latency may not affect actual traffic.

Latency increases then decreases
This can occur due to asymmetric routing, where the return path differs from the outbound path. The times displayed include the round trip, so a longer return path can inflate the displayed latency.

Permission denied
Options like -I (ICMP) and -T (TCP) require root privileges. Run the command with sudo.

FAQ

What is the difference between traceroute and ping?
ping tests whether a destination is reachable and measures round-trip latency. traceroute shows the path packets take and the latency at each hop along the route. Use ping for basic connectivity checks and traceroute for diagnosing where problems occur.

Why do some hops show asterisks?
Asterisks (* * *) mean no response was received. The router may be configured to ignore traceroute probes, a firewall may be blocking them, or the packets may have been lost. This does not necessarily mean the router is down.

What is the default protocol used by traceroute?
On Linux, traceroute uses UDP by default. On Windows, tracert uses ICMP. You can switch Linux traceroute to ICMP with -I or TCP with -T.

How do I trace the route on Windows?
Windows uses the tracert command instead of traceroute. The syntax is similar: tracert example.com. It uses ICMP by default.

What does TTL mean in traceroute?
TTL (Time to Live) is a field in the IP packet header that limits the packet’s lifespan. Each router decrements the TTL by 1. When it reaches 0, the router discards the packet and sends an ICMP “Time Exceeded” message. Traceroute uses this mechanism to discover each hop.

How can I trace the route to a specific port?
Use the -p option with TCP (-T) or UDP to specify the destination port:

Terminal
sudo traceroute -T -p 443 example.com

Is there an alternative to traceroute for continuous diagnostics?
mtr combines ping and traceroute in a single, continuously updating view and is useful for ongoing packet loss and latency checks.

Conclusion

The traceroute command is an essential tool for diagnosing network connectivity and routing issues. It shows the path packets take to a destination and helps identify where delays or failures occur.

For more options, refer to the traceroute man page by running man traceroute in your terminal.

If you have any questions, feel free to leave a comment below.

journalctl Cheatsheet

Basic Usage

View logs and follow output.

Command Description
journalctl Show all logs
journalctl -f Follow new logs (tail -f)
journalctl -n 50 Show last 50 lines
journalctl -e Jump to end of logs
journalctl -x Show explanatory messages

By Unit / Service

Filter by systemd unit.

Command Description
journalctl -u nginx Logs for a service
journalctl -u nginx --since today Logs from today
journalctl -u nginx -f Follow service logs
journalctl -u nginx -n 100 Last 100 lines for a service

Time Filters

Limit logs by time range.

Command Description
journalctl --since "2026-02-01" Logs since date
journalctl --since "1 hour ago" Logs from last hour
journalctl --since "yesterday" Logs since yesterday
journalctl --since "2026-02-01 10:00" --until "2026-02-01 12:00" Logs in time window

Boot Logs

View logs from specific boots.

Command Description
journalctl -b Current boot logs
journalctl -b -1 Previous boot logs
journalctl --list-boots List boot IDs
journalctl -b <ID> Logs for a boot ID

Priority and Kernel

Filter by severity and kernel messages.

Command Description
journalctl -p err Errors and above
journalctl -p warning Warnings and above
journalctl -p 3 Priority number (0-7)
journalctl -k Kernel messages
journalctl -k -b Kernel messages for current boot

Output Formats

Change how logs are displayed.

Command Description
journalctl -o short-iso ISO timestamps
journalctl -o short-precise High precision timestamps
journalctl -o json JSON output
journalctl -o json-pretty Pretty JSON

Disk Usage and Cleanup

Check size and vacuum old logs.

Command Description
journalctl --disk-usage Show journal size
journalctl --vacuum-size=1G Limit journal to 1 GB
journalctl --vacuum-time=7d Keep logs for 7 days
journalctl --vacuum-files=10 Keep last 10 journal files

systemctl Cheatsheet

Service Control

Start, stop, and restart services.

Command Description
systemctl start nginx Start a service
systemctl stop nginx Stop a service
systemctl restart nginx Restart a service
systemctl reload nginx Reload configuration without full restart
systemctl try-restart nginx Restart only if running
systemctl reload-or-restart nginx Reload or restart if reload unsupported

Enable at Boot

Control whether a service starts automatically.

Command Description
systemctl enable nginx Enable at boot
systemctl disable nginx Disable at boot
systemctl enable --now nginx Enable and start immediately
systemctl disable --now nginx Disable and stop immediately
systemctl is-enabled nginx Check if enabled

Status and State

Check service state and details.

Command Description
systemctl status nginx Show status and recent logs
systemctl is-active nginx Check if active
systemctl is-failed nginx Check if failed
systemctl show nginx Show unit properties
systemctl show -p ExecStart nginx Show a specific property

List Units

Find units and see what is running.

Command Description
systemctl list-units List active units
systemctl list-units --type=service List active services
systemctl list-units --all List all units
systemctl list-unit-files List unit files and enablement
systemctl list-unit-files --type=service List service unit files

Unit Files

Inspect and edit unit files.

Command Description
systemctl cat nginx Show unit file content
systemctl edit nginx Create override drop-in
systemctl edit --full nginx Edit full unit file
systemctl daemon-reload Reload systemd units after changes

Masking and Dependencies

Prevent services from starting or inspect dependencies.

Command Description
systemctl mask nginx Prevent a unit from starting
systemctl unmask nginx Remove mask
systemctl list-dependencies nginx Show dependencies
systemctl list-dependencies --reverse nginx Show reverse dependencies

System Actions

System-wide power and state controls.

Command Description
systemctl reboot Reboot the system
systemctl poweroff Power off the system
systemctl halt Halt the system
systemctl suspend Suspend
systemctl hibernate Hibernate

How to Install Google Chrome Web Browser on Ubuntu 24.04

Google Chrome is a fast and secure web browser built for the modern web. It is available for all major operating systems and allows you to sync bookmarks, history, and passwords across devices.

This guide explains how to install Google Chrome on Ubuntu 24.04 using the official Google package and repository.

Info
The official Google Chrome .deb package is available for 64-bit x86 (amd64) systems only. On ARM devices, use Chromium or another ARM-compatible browser.

Quick Reference

Task Command
Download package wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
Install package sudo apt install ./google-chrome-stable_current_amd64.deb
Start Chrome google-chrome
Set as default browser xdg-settings set default-web-browser google-chrome.desktop
Update Chrome sudo apt update && sudo apt upgrade
Uninstall Chrome sudo apt remove google-chrome-stable
Verify repo file cat /etc/apt/sources.list.d/google-chrome.list

Installing Google Chrome on Ubuntu

Chrome is not open source and is not included in the standard Ubuntu repositories. The official .deb package adds the Google signing key and repository so Chrome stays updated automatically.

Download Google Chrome

Open a terminal and use wget to download the latest stable package:

Terminal
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb

Install Google Chrome

Install the package with apt. Running this command requires sudo privileges :

Terminal
sudo apt install ./google-chrome-stable_current_amd64.deb

When prompted, enter your password to complete the installation.

Starting Google Chrome

Open the Activities overview by pressing the Super key, search for “Google Chrome”, and launch it:

Open Google Chrome on Ubuntu 24.04

You can also start Chrome from the terminal:

Terminal
google-chrome

When you start Chrome for the first time, you will be asked whether you want to set it as the default browser and enable crash reports:

Google Chrome default browser prompt on Ubuntu 24.04

Chrome then opens the welcome page:

Google Chrome welcome page on Ubuntu 24.04

From here, you can sign in with your Google account and sync your settings.

Set Chrome as Default Browser

To set Chrome as the default browser from the command line, run:

Terminal
xdg-settings set default-web-browser google-chrome.desktop

To verify the current default browser:

Terminal
xdg-settings get default-web-browser
output
google-chrome.desktop

Updating Google Chrome

During installation, the official Google repository is added to your system. Verify the repository file with cat :

Terminal
cat /etc/apt/sources.list.d/google-chrome.list

Example output:

output
### THIS FILE IS AUTOMATICALLY CONFIGURED ###
# You may comment out this entry, but any other modifications may be lost.
deb [arch=amd64] https://dl.google.com/linux/chrome/deb/ stable main

Chrome updates are delivered through the standard Ubuntu update process:

Terminal
sudo apt update && sudo apt upgrade

Uninstalling Google Chrome

To remove Google Chrome from your system, run:

Terminal
sudo apt remove google-chrome-stable

Then clean up unused dependencies:

Terminal
sudo apt autoremove

Troubleshooting

The installation fails with dependency errors
Fix broken dependencies and re-run the install:

Terminal
sudo apt --fix-broken install
sudo apt install ./google-chrome-stable_current_amd64.deb

Chrome does not start after installation
Close any running Chrome processes and start it again:

Terminal
pkill -f chrome
google-chrome

Repository file is missing
Reinstall the package to recreate the repository file:

Terminal
sudo apt install ./google-chrome-stable_current_amd64.deb

FAQ

What is the difference between Chrome and Chromium?
Chromium is the open-source project that Chrome is built on. Chrome adds proprietary features such as automatic updates, licensed media codecs (AAC, H.264), and tighter Google account integration.

How do I import bookmarks from another browser?
Open Chrome, go to Settings > Import bookmarks and settings, and select the browser you want to import from.

Can I install Chrome Beta or Dev channels?
Yes. Replace google-chrome-stable with google-chrome-beta or google-chrome-unstable in the download URL and install command.

Conclusion

Google Chrome installs on Ubuntu 24.04 using the official .deb package, which also configures the Google repository for automatic updates.

If you have any questions, feel free to leave a comment below.

Python Switch Case Statement (match-case)

Unlike many other programming languages, Python does not have a traditional switch-case statement. Before Python 3.10, developers used if-elif-else chains or dictionary lookups to achieve similar functionality.

Python 3.10 introduced the match-case statement (also called structural pattern matching), which provides a cleaner way to handle multiple conditions.

This article explains how to implement switch-case behavior in Python using all three approaches with practical examples.

Using if-elif-else

The if-elif-else chain is the most straightforward way to handle multiple conditions. It works in all Python versions and is best suited for a small number of conditions.

Here is an example:

py
def get_day_type(day):
 if day == "Saturday" or day == "Sunday":
 return "Weekend"
 elif day == "Monday":
 return "Start of the work week"
 elif day == "Friday":
 return "End of the work week"
 else:
 return "Midweek"

print(get_day_type("Saturday"))
print(get_day_type("Monday"))
print(get_day_type("Wednesday"))
output
Weekend
Start of the work week
Midweek

The function checks each condition in order. When a match is found, it returns the result and stops. If none of the conditions match, the else block runs.

This approach is easy to read and debug, but it gets verbose when you have many conditions.

Using Dictionary Lookup

A dictionary can map values to results or functions, acting as a lookup table. This approach is more concise than if-elif-else when you have many simple mappings.

In the following example, we are using a dictionary to map HTTP status codes to their descriptions:

py
def http_status(code):
 statuses = {
 200: "OK",
 301: "Moved Permanently",
 404: "Not Found",
 500: "Internal Server Error",
 }
 return statuses.get(code, "Unknown Status")

print(http_status(200))
print(http_status(404))
print(http_status(999))
output
OK
Not Found
Unknown Status

The get() method returns the value for the given key. If the key is not found, it returns the default value ("Unknown Status").

You can also map keys to functions. In the code below, we are creating a simple calculator using a dictionary of functions:

py
def add(a, b):
 return a + b

def subtract(a, b):
 return a - b

def multiply(a, b):
 return a * b

operations = {
 "+": add,
 "-": subtract,
 "*": multiply,
}

func = operations.get("+")
print(func(10, 5))
output
15

Dictionary lookups are fast and scale well, but they cannot handle complex conditions like ranges or pattern matching.

Using match-case (Python 3.10+)

The match-case statement was introduced in Python 3.10 . It compares a value against a series of patterns and executes the matching block.

The match-case statement takes the following form:

py
match expression:
 case pattern1:
 statements
 case pattern2:
 statements
 case _:
 default statements

The match keyword is followed by the expression to evaluate. Each case defines a pattern to match against. The case _ is the wildcard (default) case that matches any value not matched by previous patterns, similar to default in other languages.

Here is a basic example:

py
def http_error(status):
 match status:
 case 400:
 return "Bad Request"
 case 401 | 403:
 return "Not Allowed"
 case 404:
 return "Not Found"
 case _:
 return "Unknown Error"

print(http_error(403))
print(http_error(404))
print(http_error(999))
output
Not Allowed
Not Found
Unknown Error

You can combine multiple values in a single case using the | (or) operator, as shown with 401 | 403.

Let’s look at the different pattern matching capabilities of the match-case statement.

Matching with Variables

You can capture values from the matched expression and use them in the case block. In the following example, we are matching a tuple representing a point on a coordinate plane:

py
point = (3, 7)

match point:
 case (0, 0):
 print("Origin")
 case (x, 0):
 print(f"On x-axis at {x}")
 case (0, y):
 print(f"On y-axis at {y}")
 case (x, y):
 print(f"Point at ({x}, {y})")
output
Point at (3, 7)

The variables x and y are assigned the values from the tuple when the pattern matches.

Matching with Guards

You can add an if condition (called a guard) to a case pattern for more precise matching:

py
def classify_age(age):
 match age:
 case n if n < 0:
 return "Invalid"
 case n if n < 18:
 return "Minor"
 case n if n < 65:
 return "Adult"
 case _:
 return "Senior"

print(classify_age(10))
print(classify_age(30))
print(classify_age(70))
output
Minor
Adult
Senior

The guard (if n < 18) adds an extra condition that must be true for the case to match.

Matching Dictionaries

The match-case statement can match against dictionary structures, which is useful when working with JSON data or API responses:

py
def process_command(command):
 match command:
 case {"action": "create", "name": name}:
 print(f"Creating {name}")
 case {"action": "delete", "name": name}:
 print(f"Deleting {name}")
 case {"action": action}:
 print(f"Unknown action: {action}")

process_command({"action": "create", "name": "users"})
process_command({"action": "delete", "name": "logs"})
process_command({"action": "update"})
output
Creating users
Deleting logs
Unknown action: update

The pattern only needs to match the specified keys. Extra keys in the dictionary are ignored.

Which Approach to Use

Approach Best For Python Version
if-elif-else Few conditions, complex boolean logic All versions
Dictionary lookup Many simple value-to-value mappings All versions
match-case Pattern matching, destructuring, complex data 3.10+

Use if-elif-else when you have a handful of conditions or need complex boolean expressions. Use dictionary lookups when you are mapping values directly. Use match-case when you need to match against data structures, capture variables, or use guard conditions.

Quick Reference

py
# if-elif-else
if x == 1:
 result = "one"
elif x == 2:
 result = "two"
else:
 result = "other"

# Dictionary lookup
result = {1: "one", 2: "two"}.get(x, "other")

# match-case (Python 3.10+)
match x:
 case 1:
 result = "one"
 case 2:
 result = "two"
 case _:
 result = "other"

FAQ

Does Python have a switch statement?
Not a traditional one. Python uses if-elif-else chains, dictionary lookups, or the match-case statement (Python 3.10+) to achieve similar functionality.

What Python version do I need for match-case?
Python 3.10 or later. If you need to support older versions, use if-elif-else or dictionary lookups instead.

Is match-case faster than if-elif-else?
For most use cases the performance difference is negligible. Choose based on readability and the complexity of your conditions, not speed. Dictionary lookups are often the fastest for simple value mappings.

What happens if no case matches and there is no wildcard?
Nothing. If no pattern matches and there is no case _, the match statement completes without executing any block. No error is raised.

Can I use match-case with classes?
Yes. You can match against class instances using the case ClassName(attr=value) syntax. This is useful for handling different object types in a clean way.

Conclusion

Python does not have a built-in switch statement, but offers three alternatives. Use if-elif-else for simple conditions, dictionary lookups for direct value mappings, and match-case for pattern matching on complex data structures.

For more Python tutorials, see our guides on for loops , while loops , and dictionaries .

If you have any questions, feel free to leave a comment below.

Sed Cheatsheet

Syntax

General command forms.

Command Description
sed 'script' file Run sed script on a file
sed -n 'script' file Suppress auto-print, print only with p
printf '%s\n' "text" | sed 'script' Read from stdin
sed -e 'cmd1' -e 'cmd2' file Multiple commands
sed -f script.sed file Read commands from file

Options

Common CLI flags.

Command Description
sed -n 'script' file Suppress auto-print
sed -E 'script' file Extended regex (GNU and BSD)
sed -r 'script' file Extended regex (GNU only)
sed -i 'script' file Edit in place (GNU sed)
sed -i.bak 'script' file Edit in place with backup

Substitution

Replace text with s/old/new/.

Command Description
sed 's/old/new/' file Replace first match on each line
sed 's/old/new/g' file Replace all matches
sed 's/old/new/2' file Replace second match
sed 's/old/new/Ig' file Case-insensitive replace (GNU sed)
`sed ’s /usr
sed -n 's/old/new/p' file Print only lines with replacements
sed 's/old/new/w out.txt' file Write changed lines to file

Addresses

Apply commands to specific lines.

Command Description
sed '3s/a/b/' file Substitute on line 3 only
sed '1,5s/a/b/' file Lines 1 through 5
sed '/pattern/s/a/b/' file Lines matching pattern
sed '3,/pattern/s/a/b/' file Line 3 through first match
sed '/start/,/end/d' file Delete range between patterns

Print And Delete

Control output and remove lines.

Command Description
sed -n 'p' file Print all lines (same as cat)
sed -n '3p' file Print line 3 only
sed -n '/pattern/p' file Print matching lines
sed -n '1,5p' file Print range of lines
sed 'd' file Delete all lines (prints nothing)
sed '3d' file Delete line 3
sed '/pattern/d' file Delete matching lines

Insert, Append, Change

Add or replace whole lines.

Command Description
sed '2i\\new line' file Insert before line 2
sed '2a\\new line' file Append after line 2
sed '2c\\new line' file Replace line 2
sed '/pattern/i\\new line' file Insert before matches
sed '/pattern/a\\new line' file Append after matches

Other Commands

Useful non-substitution commands.

Command Description
sed 'y/abc/xyz/' file Translate characters
sed '=' file Print line numbers
sed 'q' file Quit after first line
sed '3q' file Quit after line 3
sed 'n' file Read next line, skip current output
sed 'N' file Append next line to pattern space
sed '/pattern/r other.txt' file Read file after matches
sed '/pattern/w out.txt' file Write matching lines to file

In-Place Editing

Write changes back to files.

Command Description
sed -i 's/old/new/g' file Edit in place (GNU sed)
sed -i.bak 's/old/new/g' file Edit in place with backup
sed -i '' 's/old/new/g' file Edit in place on macOS/BSD
sed -i -e 's/a/b/' -e 's/c/d/' file Multiple edits in place

Regular Expressions

Use regex and capture groups.

Command Description
sed 's/[0-9]\{4\}/YEAR/' file Replace 4-digit numbers
sed -E 's/[0-9]{4}/YEAR/g' file Extended regex (GNU and BSD)
sed 's/\(foo\)bar/\1baz/' file Capture group (basic)
sed -E 's/(foo)bar/\1baz/' file Capture group (extended)
sed -E 's#(https?)://#\1://#' file Use groups and alternate delimiter

Line Selection

Select by position or pattern.

Command Description
sed -n '1p' file First line
sed -n '$p' file Last line
sed -n '1~2p' file Every 2nd line (GNU sed)
sed -n '/error/,+2p' file Match plus next 2 lines
sed -n '/start/,/end/p' file Print between patterns

Common Patterns

Useful one-liners.

Command Description
sed 's/[[:space:]]\+$//' file Trim trailing whitespace
sed 's/^[[:space:]]\+//' file Trim leading whitespace
sed 's/[[:space:]]\+/ /g' file Collapse whitespace
sed '/^#/d;/^$/d' file Remove comments and blank lines
sed -n 'n;p' file Print even lines

How to Fix SSH "Permission Denied (publickey)" Error

The “Permission denied (publickey)” error occurs when the SSH server rejects your connection because it cannot verify your identity using public key authentication.

output
user@remote_host: Permission denied (publickey).

This is one of the most common SSH errors. In this guide, we will go through the most frequent causes and how to fix them.

Debugging the Connection

Before trying specific fixes, run SSH in verbose mode to see exactly where the authentication fails:

Terminal
ssh -v user@remote_host

For even more detail, use -vv or -vvv:

Terminal
ssh -vvv user@remote_host

Look for lines like:

output
debug1: Offering public key: /home/user/.ssh/id_ed25519
debug1: Server accepts key: /home/user/.ssh/id_ed25519

or:

output
debug1: Trying private key: /home/user/.ssh/id_ed25519
debug1: No more authentication methods to try.

The verbose output tells you which keys are being tried and where the process fails.

Common Causes and Fixes

1. Public Key Not on the Remote Server

The most common cause is that your public key is not in the remote server’s ~/.ssh/authorized_keys file.

Copy your public key to the server:

Terminal
ssh-copy-id user@remote_host

If ssh-copy-id is not available, copy it manually:

Terminal
cat ~/.ssh/id_ed25519.pub | ssh user@remote_host "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

For more details, see our guide on how to copy SSH keys with ssh-copy-id .

2. Wrong File Permissions

SSH is strict about file permissions. If the permissions are too open, the SSH server will reject the key.

On the remote server, set the correct permissions:

Terminal
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

On the local machine, set the correct permissions on your private key:

Terminal
chmod 600 ~/.ssh/id_ed25519

The SSH server will also reject keys if the home directory is writable by others:

Terminal
chmod 755 ~

These checks are enforced by the StrictModes yes setting in /etc/ssh/sshd_config, which is enabled by default. Do not disable it — fix the permissions instead.

3. Wrong User or Hostname

Make sure you are connecting as the correct user. The key must be in that user’s authorized_keys file:

Terminal
ssh deploy@remote_host

If you have a host alias in your SSH config file , verify it points to the correct hostname and user.

4. SSH Agent Not Running

If your key is protected with a passphrase and the SSH agent is not running, the key will not be offered to the server.

Start the SSH agent and add your key:

Terminal
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

To list the keys currently loaded in the agent:

Terminal
ssh-add -l

5. Wrong Key Being Used

If you have multiple SSH keys, the client may be offering the wrong one. Specify the key explicitly:

Terminal
ssh -i ~/.ssh/id_ed25519 user@remote_host

Or configure it in your ~/.ssh/config:

~/.ssh/configini
Host remote_host
 HostName 192.168.1.10
 User deploy
 IdentityFile ~/.ssh/id_ed25519

6. Public Key Authentication Disabled on the Server

If public key authentication is disabled on the server, key-based login will not work regardless of your key setup. Check /etc/ssh/sshd_config on the server:

ini
PasswordAuthentication no
PubkeyAuthentication yes

If PubkeyAuthentication is set to no, key-based login will not work. Change it to yes and restart SSH:

Terminal
sudo systemctl restart sshd

7. Key Type Not Accepted

Some servers are configured to accept only certain key types. If your key type is not allowed, the server will reject it.

Check the PubkeyAcceptedAlgorithms setting in /etc/ssh/sshd_config:

ini
PubkeyAcceptedAlgorithms +ssh-rsa

If you are using an older RSA key, the server may require you to explicitly allow it. This should be a last resort because ssh-rsa is deprecated on modern OpenSSH. A better solution is to generate a new Ed25519 key:

Terminal
ssh-keygen -t ed25519

8. SELinux Blocking Access

On systems with SELinux enabled (RHEL, CentOS, Fedora), SELinux may prevent the SSH server from reading the authorized_keys file.

Check for SELinux denials:

Terminal
sudo ausearch -m avc -ts recent

Restore the correct SELinux context:

Terminal
sudo restorecon -R ~/.ssh

9. Home Directory on Network Storage

If the user’s home directory is on NFS or another network filesystem, the SSH server may not be able to read the authorized_keys file before the filesystem is mounted.

You can configure an alternative location for authorized keys in /etc/ssh/sshd_config:

ini
AuthorizedKeysFile /etc/ssh/authorized_keys/%u

Then place the user’s keys in /etc/ssh/authorized_keys/username.

Troubleshooting

Server-Side Debugging

If you have access to the server, check the authentication log for error details:

On Ubuntu and Debian:

Terminal
sudo tail -f /var/log/auth.log

On CentOS, RHEL, and Fedora:

Terminal
sudo tail -f /var/log/secure

You can also run the SSH server in debug mode temporarily on a different port:

Terminal
sudo /usr/sbin/sshd -d -p 2222

Then connect to it from the client:

Terminal
ssh -p 2222 user@remote_host

The server will print detailed authentication information to the terminal.

Quick Reference

Problem Fix
Key not on server ssh-copy-id user@host
Wrong permissions on ~/.ssh chmod 700 ~/.ssh
Wrong permissions on authorized_keys chmod 600 ~/.ssh/authorized_keys
Wrong permissions on private key chmod 600 ~/.ssh/id_ed25519
SSH agent not running eval "$(ssh-agent -s)" && ssh-add
Wrong key offered ssh -i ~/.ssh/key user@host
SELinux blocking access sudo restorecon -R ~/.ssh
Check auth log (Ubuntu) sudo tail -f /var/log/auth.log
Debug SSH connection ssh -vvv user@host

FAQ

Why does SSH care about file permissions?
SSH refuses to use keys with overly permissive file permissions because other users on the system could read your private key or modify your authorized_keys file. This is a security feature, not a bug.

I copied my key but it still doesn’t work. What should I check?
Check the permissions on the remote ~/.ssh directory (700), the authorized_keys file (600), and the home directory (755 or stricter). Also verify you are connecting as the correct user.

How do I check which keys the SSH client is trying?
Run ssh -v user@host and look for lines starting with debug1: Offering public key or debug1: Trying private key. This shows which keys are being offered to the server.

Does this error always mean a key problem?
Not always. If the server has PasswordAuthentication no and PubkeyAuthentication no, all authentication methods are disabled and you will see this error regardless.

Can I temporarily enable password login to fix the key?
Yes. If you have console access, set PasswordAuthentication yes in /etc/ssh/sshd_config, restart SSH, copy your key with ssh-copy-id, then disable password authentication again.

I get this error on an AWS, GCP, or Azure instance. What should I check?
Cloud instances use a specific key pair set during provisioning. Make sure you are using that key: ssh -i ~/.ssh/my-cloud-key.pem user@host. The default username also varies by provider (e.g., ec2-user on Amazon Linux, ubuntu on Ubuntu instances, azureuser on Azure). Check your provider’s documentation for the correct username.

Conclusion

The “Permission denied (publickey)” error is almost always caused by a missing key, wrong file permissions, or the SSH client offering the wrong key. Start by running ssh -v to identify the problem, then work through the fixes above.

If you have any questions, feel free to leave a comment below.

How to Install Python on Ubuntu 24.04

Python is one of the most popular programming languages. It is used to build all kinds of applications, from simple scripts to complex machine-learning systems. With its straightforward syntax, Python is a good choice for both beginners and experienced developers.

Ubuntu 24.04 ships with Python 3.12 preinstalled. To check the version on your system:

Terminal
python3 --version
output
Python 3.12.3

If you need a newer Python version such as 3.13 or 3.14, you can install it from the deadsnakes PPA or build it from source. Both methods install the new version alongside the system Python without replacing it. This guide shows how to install Python on Ubuntu 24.04 using both approaches.

Quick Reference

Task Command
Check installed Python version python3 --version
Add deadsnakes PPA sudo add-apt-repository ppa:deadsnakes/ppa
Install Python 3.13 from PPA sudo apt install python3.13
Install Python 3.14 from PPA sudo apt install python3.14
Install venv module sudo apt install python3.13-venv
Build from source (configure) ./configure --enable-optimizations
Build from source (compile) make -j $(nproc)
Install from source sudo make altinstall
Create a virtual environment python3.13 -m venv myproject
Activate virtual environment source myproject/bin/activate
Deactivate virtual environment deactivate

Installing Python from the Deadsnakes PPA

The deadsnakes PPA provides newer Python versions packaged for Ubuntu. This is the easiest way to install a different Python version.

  1. Install the prerequisites and add the PPA:

    Terminal
    sudo apt update
    sudo apt install software-properties-common
    sudo add-apt-repository ppa:deadsnakes/ppa

    Press Enter when prompted to confirm.

  2. Install Python 3.13:

    Terminal
    sudo apt update
    sudo apt install python3.13

    To install Python 3.14 instead, replace python3.13 with python3.14 in the command above.

  3. Verify the installation:

    Terminal
    python3.13 --version
    output
    Python 3.13.11

    You can also confirm the binary location:

    Terminal
    which python3.13
  4. Install the venv module (needed for creating virtual environments):

    Terminal
    sudo apt install python3.13-venv
  5. If you need pip, install the venv package and create a virtual environment, then use pip inside the venv:

    Terminal
    python3.13 -m venv myproject
    source myproject/bin/activate
    python -m pip install --upgrade pip

    If you need a system-level pip for that interpreter, run:

    Terminal
    python3.13 -m ensurepip --upgrade
Info
The system default python3 still points to Python 3.12. To use the newly installed version, run python3.13 or python3.14 explicitly.

Installing Python from Source

Compiling Python from source allows you to install any version and customize the build options. However, you will not be able to manage the installation through the apt package manager.

The following steps show how to compile Python 3.13. If you are installing a different version, replace the version number in the commands below.

  1. Install the libraries and dependencies required to build Python:

    Terminal
    sudo apt update
    sudo apt install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev wget libbz2-dev liblzma-dev
  2. Download the source code from the Python download page using wget :

    Terminal
    wget https://www.python.org/ftp/python/3.13.11/Python-3.13.11.tgz
  3. Extract the archive :

    Terminal
    tar -xf Python-3.13.11.tgz
  4. Navigate to the source directory and run the configure script:

    Terminal
    cd Python-3.13.11
    ./configure --enable-optimizations

    The --enable-optimizations flag runs profile-guided optimization tests, which makes the build slower but produces a faster Python binary.

  5. Start the build process:

    Terminal
    make -j $(nproc)

    The -j $(nproc) option uses all available CPU cores for a faster build.

  6. Install the Python binaries using altinstall. Do not use install, as it overwrites the system python3 binary and can break system tools that depend on it:

    Terminal
    sudo make altinstall
  7. Verify the installation:

    Terminal
    python3.13 --version
    output
    Python 3.13.11

Setting Up a Virtual Environment

After installing a new Python version, create a virtual environment for your project to keep dependencies isolated:

Terminal
python3.13 -m venv myproject

Activate the virtual environment:

Terminal
source myproject/bin/activate

Your shell prompt will change to show the environment name. Inside the virtual environment, python and pip point to the version you used to create it.

To deactivate the virtual environment:

Terminal
deactivate

For more details, see our guide on how to create Python virtual environments .

Uninstalling the PPA Version (Optional)

To remove the PPA version:

Terminal
sudo apt remove python3.13 python3.13-venv

To remove the PPA itself:

Terminal
sudo add-apt-repository --remove ppa:deadsnakes/ppa

FAQ

Should I use the PPA or build from source?
The deadsnakes PPA is the recommended method for most users. It is easier to install, receives security updates through apt, and does not require build tools. Build from source only if you need a custom build configuration or a version not available in the PPA.

Will installing a new Python version break my system?
No. Both methods install the new version alongside the system Python 3.12. The system python3 command is not affected. You access the new version with python3.13 or python3.14.

How do I make the new Python version the default?
You can use update-alternatives to configure it, but this is not recommended. Many Ubuntu system tools depend on the default python3 being the version that shipped with the OS. Use virtual environments instead.

How do I install pip for the new Python version?
The recommended approach is to use a virtual environment. Install python3.13-venv, create a venv, and use pip inside it. If you need a system-level pip for that interpreter, run python3.13 -m ensurepip --upgrade.

What is the difference between install and altinstall when building from source?
altinstall installs the binary as python3.13 without creating a python3 symlink. install creates the symlink, which overwrites the system Python and can break Ubuntu system tools.

Does Ubuntu 24.04 include pip by default?
Ubuntu 24.04 includes Python 3.12 but does not include pip in the base installation. Install it with sudo apt install python3-pip. For newer Python versions, use python3.13 -m pip inside a virtual environment.

Conclusion

Ubuntu 24.04 ships with Python 3.12. To install a newer version, use the deadsnakes PPA for a simple apt-based installation, or build from source for full control over the build. Use virtual environments to manage project dependencies without affecting the system Python.

For more on Python package management, see our guide on how to use pip .

If you have any questions, feel free to leave a comment below.

Rsync Cheatsheet

Install

Command Description
sudo apt install rsync Install on Debian/Ubuntu
sudo yum install rsync Install on RHEL/CentOS
sudo dnf install rsync Install on Fedora

Basic Syntax

Command Description
rsync [OPTION] SRC DEST Local sync
rsync [OPTION] SRC USER@HOST:DEST Local → remote
rsync [OPTION] USER@HOST:SRC DEST Remote → local

Local Sync Examples

Command Description
rsync -a src/ dest/ Mirror contents
rsync -a src dest/ Copy dir into dest
rsync -av src/ dest/ Verbose sync
rsync -a --delete src/ dest/ Delete extra files

Remote over SSH

Command Description
rsync -a src/ user@host:/path/ Local → remote
rsync -a user@host:/path/ dest/ Remote → local
rsync -a -e "ssh -p 2222" src/ user@host:/path/ Custom SSH port
rsync -a -P src/ user@host:/path/ Progress + keep partial

Common Options

Option Description
-a Archive (recursive + preserve)
-v Verbose
-z Compress during transfer
-P Progress + partial files
--delete Delete extras in dest
--dry-run Show changes only

Excluding Files

Command Description
rsync -a --exclude 'node_modules' src/ dest/ Exclude by name
rsync -a --exclude-from='exclude.txt' src/ dest/ Exclude from file
rsync -a --include='*.jpg' --exclude='*' src/ dest/ Include pattern

Ownership & Permissions

Option Description
-p Preserve permissions
-o Preserve owner (root)
-g Preserve group
--chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r Set permissions

Handy Patterns

Command Description
rsync -a --info=progress2 src/ dest/ Overall progress
rsync -a --bwlimit=5m src/ dest/ Limit bandwidth
rsync -a --partial --append-verify src/ dest/ Resume large files
rsync -a --stats src/ dest/ Summary stats
❌