Ubuntu OS Operations
Skills and command 101
Hardware
Check disk I/O speed
sudo dd if=/dev/zero of=/testfile bs=1G count=1 oflag=direct
sudo dd if=/testfile of=/dev/null bs=1G count=1 iflag=direct
sudo rm /testfile
sudo apt update && sudo apt install hdparm
sudo hdparm -Tt /dev/sda
sudo apt install sysstat
iostat -xz 2
Clone damaged disk
Reallocated Sectors: 90 suddenly increase and gives errors I/O input error. I wish to backup data to another disk any change to lost data?
Yes, there is a strong chance that some data is already lost or at immediate risk.
A sudden spike in reallocated sectors accompanied by I/O errors indicates active physical degradation on the disk media. Standard file copiers (like Windows Explorer or basic cp) will hang, freeze, or fail completely when encountering these sectors, worsening drive health in the process.
What Is Happening to Your Disk
Critical Next Steps (Do Not Use Standard Copying)
Do not run chkdsk, fsck, or drive diagnostic tools like S.M.A.R.T. extended self-tests. These perform heavy read/write operations that can destroy a failing drive.
1. Prioritize Crucial Files First If the drive is still mounted, manually copy your absolute highest-value files (documents, personal records) individually to a healthy drive before doing anything else.
2. Clone the Drive with a Fault-Tolerant Tool To rescue the maximum amount of data without hanging your system on bad blocks, clone the entire drive to a healthy disk of equal or larger size using a tool designed to skip bad sectors:
3. Unmount and Replace Once the clone completes, disconnect the failing drive immediately. Perform any file system repair or data recovery operations on the cloned copy, never the original failing drive.
If the drive begins making clicking noises or ceases to mount entirely during this process, power it off immediately; at that point, only a professional data recovery lab with cleanroom facilities can safely extract the remaining magnetic data.
oh my os is ubunut and Can I clone my data to another machine using tailscale?
Yes, you can clone the drive over Tailscale, but because of the failing state of the drive (I/O errors), you must use a tool that streams block-level data while skipping bad sectors over an SSH tunnel. Standard network copy tools (scp, rsync) will freeze or fail when they hit bad blocks.
Step-by-Step Recovery over Tailscale
1. Install GNU ddrescue on the Local Machine
Install the fault-tolerant recovery tool on the Ubuntu machine with the dying drive:
sudo apt update && sudo apt install gddrescue -y
2. Identify Drive Device Names
Find the device node for your failing drive (e.g., /dev/sdb or /dev/nvme0n1):
lsblk
(Verify carefully—do not mistake your system root drive for the dying drive).
3. Stream ddrescue to the Remote Machine over Tailscale
Run ddrescue on the local machine and pipe its output through SSH to write directly into an image file on the remote machine across your Tailnet:
sudo ddrescue -d -r1 /dev/sdX - remote_user@REMOTE-TAILSCALE-IP:/path/to/backup/drive_rescue.img mapfile.log
Important Considerations for Network Recovery
ssh port is 2010
To use port 2010, pass the -P parameter (capital P) inside the sshfs mount command or the -p parameter (lowercase p) if piping directly through ssh.
Using the SSHFS method is strongly recommended here—it keeps your rescue.log mapfile local so you can safely stop and resume the clone if your Tailscale connection drops.
Method 1: Recommended (SSHFS Mount on Port 2010)
1. Create a local mount point and mount the target machine:
mkdir -p /mnt/remote_backup
sshfs -p 2010 remote_user@REMOTE-TAILSCALE-IP:/path/to/backup /mnt/remote_backup
2. Start ddrescue writing directly into the mount:
sudo ddrescue -d -r1 /dev/sdX /mnt/remote_backup/drive_rescue.img /var/log/rescue.log
(Replace /dev/sdX with your failing drive, and update remote_user and REMOTE-TAILSCALE-IP accordingly).
Method 2: Direct SSH Stream on Port 2010
If you prefer streaming directly without mounting SSHFS, pass -p 2010 to the ssh command:
sudo ddrescue -d -r1 /dev/sdX - | ssh -p 2010 remote_user@REMOTE-TAILSCALE-IP "cat > /path/to/backup/drive_rescue.img"
Unmounting When Finished
When the cloning process completes, clean up the mount with:
sudo umount /mnt/remote_backup
img is how big ?
The .img file will be the exact total capacity of the dying source disk, not just the size of the files currently stored on it.
Because ddrescue creates a raw, sector-by-sector clone of the underlying physical storage, it copies every byte on the drive—including empty space and corrupted sectors.
Disk Capacity vs. File Size
Example: If you have a 1 TB hard drive that only contains 50 GB of personal photos and documents, the resulting drive_rescue.img file will still be 1 TB.
Crucial Pre-Flight Check
Before starting the transfer, verify that the storage drive on your remote machine has more free space available than the total size of your failing disk.
1. Check source drive size (local machine):
lsblk -b /dev/sdX | grep disk
(Look at the total size in bytes/gigabytes).
2. Check destination free space (remote machine over SSH port 2010):
ssh -p 2010 remote_user@REMOTE-TAILSCALE-IP "df -h /path/to/backup"
(Ensure the Avail column is larger than the total size of your source drive).
What to Do After the .img File Is Created
Once the clone finishes, do not try to open the .img file like a standard archive. You can mount it directly on the remote Linux machine to browse and copy your healthy files out:
sudo loopsetup=$(sudo losetup -fP --show /path/to/backup/drive_rescue.img)
sudo mount ${loopsetup}p1 /mnt/restored_files
(Replace p1 with the specific partition number you want to access).
Can I just clone one drive to another drive?
Yes, absolutely. Cloning directly from disk to disk (e.g., from /dev/sdX to /dev/sdY) is actually simpler and much faster because it avoids network bottlenecks, SSH overhead, and intermediate image files.
Prerequisites
Step-by-Step Disk-to-Disk Rescue
1. Identify your drives:
lsblk -o NAME,SIZE,MODEL,SERIAL
2. Run ddrescue directly between the drives:
sudo ddrescue -d -r1 /dev/sdX /dev/sdY /var/log/rescue.log
What Happens Next?
Once ddrescue finishes:
Target disk sde 931.5G Tech K94PT9325BHV
Source disk sda 931.5G D HTS541010A9E63 000000004BA8
Your drive sizes match (both 931.5 GB), so a direct disk-to-disk clone will work.
Step 1: Run ddrescue
Execute the following command to begin copying data directly from sda to sde:
sudo ddrescue -d -r1 /dev/sda /dev/sde /var/log/sda_rescue.log
Step 2: What to Expect During the Run
Step 3: Post-Rescue Cleanup
Once ddrescue reports finished:
IS this work can be done via tailscale?
Yes, absolutely. You can execute this disk-to-disk clone across machines connected via Tailscale.
However, because you are cloning a whole 931.5 GB drive directly over the network, there are critical speed and command adjustments to keep in mind.
Step-by-Step: Disk-to-Disk Clone Over Tailscale
Assuming /dev/sda is on your Local Machine (with the dying drive) and /dev/sde is physically attached to the Remote Machine on your Tailnet.
1. Command to Run on Local Machine (Port 2010)
Pipe ddrescue directly through SSH to write onto /dev/sde on the remote side:
sudo ddrescue -d -r1 /dev/sda - | ssh -p 2010 remote_user@REMOTE-TAILSCALE-IP "sudo dd of=/dev/sde status=progress bs=64k"
Critical Considerations Before You Start
1. Transfer Time (Network Bottleneck)
Over a local disk bus (SATA/USB 3), cloning 931.5 GB takes roughly 2 to 3 hours. Over a Tailscale network connection, your speed will be limited by your upload speed:
If both machines are on the same physical local network (e.g., same router/switch), use the destination machine's local LAN IP inside the SSH command instead of the Tailscale IP to get full gigabit speeds.
2. Risk of Network Dropouts
Piping ddrescue directly over SSH standard output does not allow saving a detailed bad-sector mapfile across the network connection. If your network hiccups or SSH drops mid-transfer, you cannot resume from where you left off; you would have to start from the beginning.
Recommendation: Run the command inside a tmux or screen session on your local terminal so that a closed SSH/terminal session won't kill the running process:
tmux new -s rescue
(If disconnected, re-attach later using tmux a -t rescue)
need to format for target disk ?
No, you do not need to format the target disk (/dev/sde).
In fact, formatting it beforehand would be a wasted step. ddrescue performs a low-level, sector-by-sector copy—meaning it will overwrite every single byte on the target disk, including its partition table, file system headers, and formatting data.
What Will Happen to the Target Disk (/dev/sde)
One Important Prerequisite
While you don't need to format /dev/sde, you must ensure that none of its partitions are mounted before running the recovery command:
sudo umount /dev/sde* 2>/dev/null
This prevents the Linux kernel on the receiving machine from writing to /dev/sde while ddrescue is actively overwriting the raw drive blocks over your network stream.
41250 bytes (41 kB, 40 KiB) copied, 76 s, 0.5 kB/sddrescue: /dev/sda: Fatal error reading the input file: No such device
41720 bytes (42 kB, 41 KiB) copied, 77 s, 0.5 kB/s
0+78 records in
0+78 records out
41720 bytes (42 kB, 41 KiB) copied, 76.9141 s, 0.5 kB/s
Fatal error reading the input file: No such device means your dying hard drive (/dev/sda) has physically disconnected from the machine.
When a drive with severe bad sectors suffers repeated read failures, its internal controller crashes, or the Linux kernel resets the SATA/USB bus to prevent system lockup. As a result, the hardware completely disappears from /dev/sda. Notice how it only managed to copy ~41 kB before disconnecting.
Step 1: Check if the Drive Is Still Detected
Run this command to see if the system can still see the hardware:
lsblk
Or check kernel logs to see the drop event:
dmesg -T | grep -i "ata\|sd\|error" | tail -n 20
Step 2: How to Re-identify or Reset the Drive
If lsblk no longer lists /dev/sda:
Step 3: Crucial Strategy Adjustment for ddrescue
Your drive crashed because ddrescue attempted direct access (-d) on a severely degraded sector right at the beginning of the disk.
When you restart ddrescue, remove the -d (direct I/O) flag initially and add --scrape-members / --no-scrape options to skip damaged areas instantly without hanging the drive controller:
sudo ddrescue -n -b 4096 /dev/sdX - | ssh -p 2010 remote_user@REMOTE-TAILSCALE-IP "sudo dd of=/dev/sde status=progress bs=64k"
Warning Signs of Permanent Hardware Failure
If the drive disconnects again almost immediately after starting (within seconds or after copying just a few kilobytes):
disk is still connected and
[Mon Aug 3 13:08:38 2026] I/O error, dev sda, sector 0 op 0x0:(READ) flags 0x800 phys_seg 16 prio class 2
[Mon Aug 3 13:21:05 2026] sd 1:0:0:0: [sda] tag#1 uas_zap_pending 0 uas-tag 1 inflight: CMD
[Mon Aug 3 13:21:05 2026] sd 1:0:0:0: [sda] tag#1 CDB: Read(10) 28 00 00 b8 70 80 00 00 80 00
[Mon Aug 3 13:21:05 2026] sd 1:0:0:0: [sda] tag#1 FAILED Result: hostbyte=DID_NO_CONNECT driverbyte=DRIVER_OK cmd_age=0s
[Mon Aug 3 13:21:05 2026] sd 1:0:0:0: [sda] tag#1 CDB: Read(10) 28 00 00 b8 70 80 00 00 80 00
[Mon Aug 3 13:21:05 2026] I/O error, dev sda, sector 12087424 op 0x0:(READ) flags 0x800 phys_seg 16 prio class 2
[Mon Aug 3 13:21:05 2026] device offline error, dev sda, sector 12107136 op 0x0:(READ) flags 0x800 phys_seg 16 prio class 2
[Mon Aug 3 13:21:05 2026] sd 1:0:0:0: [sda] Synchronizing SCSI cache
[Mon Aug 3 13:21:05 2026] sd 1:0:0:0: [sda] Synchronize Cache(10) failed: Result: hostbyte=DID_ERROR driverbyte=DRIVER_OK
[Mon Aug 3 13:21:06 2026] EXT4-fs (sda): unmounting filesystem dc434a0a-8500-41a2-9fd3-37b3adef0cc5.
[Mon Aug 3 13:21:09 2026] sd 1:0:0:0: Attached scsi generic sg0 type 0
[Mon Aug 3 13:21:09 2026] sd 1:0:0:0: [sda] 1953525168 512-byte logical blocks: (1.00 TB/932 GiB)
[Mon Aug 3 13:21:09 2026] sd 1:0:0:0: [sda] 4096-byte physical blocks
[Mon Aug 3 13:21:09 2026] sd 1:0:0:0: [sda] Write Protect is off
[Mon Aug 3 13:21:09 2026] sd 1:0:0:0: [sda] Mode Sense: 53 00 00 08
[Mon Aug 3 13:21:09 2026] sd 1:0:0:0: [sda] Disabling FUA
[Mon Aug 3 13:21:09 2026] sd 1:0:0:0: [sda] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[Mon Aug 3 13:21:09 2026] sd 1:0:0:0: [sda] Preferred minimum I/O size 4096 bytes
[Mon Aug 3 13:21:09 2026] sd 1:0:0:0: [sda] Optimal transfer size 33553920 bytes not a multiple of preferred minimum block size (4096 bytes)
[Mon Aug 3 13:21:09 2026] sd 1:0:0:0: [sda] Attached SCSI disk
The logs show two very important things:
To fix this, you must disable UAS for this drive so it falls back to the legacy, rock-solid usb-storage protocol. The standard usb-storage driver handles read errors gracefully without dropping the entire device.
Step 1: Disable UAS and Force usb-storage
1. Find your USB enclosure's Vendor and Product ID:
lsusb | grep -i "terramaster\|orico\|sabrent\|enclosure\|drive"
(Or simply run lsusb to see all USB devices). Look for a string like ID 152d:0578 (where 152d is Vendor ID and 0578 is Product ID).
2. Unbind UAS temporarily using modprobe: Run this command (replace 152d:0578 with your actual IDs):
sudo modprobe -r uas
sudo modprobe usb-storage quirks=152d:0578:u
(The :u flag forces Linux to ignore UAS and use standard usb-storage for this device).
3. Unplug and replug the USB cable. Verify it dropped UAS by checking dmesg:
dmesg | tail -n 15
You should see USB Mass Storage device detected instead of UAS driver.
Step 2: Disable Automatic Mounting
Notice line 13:21:06 in your log showing EXT4-fs (sda): unmounting. Ubuntu tried to automount the file system, which triggers immediate background reads on corrupted sectors.
Prevent Ubuntu from automounting or touching the partition structure while cloning:
gsettings set org.gnome.desktop.media-handling automount false
Step 3: Run ddrescue with Gentle Flags
Now that UAS is disabled, retry cloning over Tailscale. Use the -n (--no-scrape) option and a 4096-byte block size so ddrescue quickly skips bad blocks instead of getting stuck:
sudo ddrescue -n -b 4096 /dev/sda - | ssh -p 2010 remote_user@REMOTE-TAILSCALE-IP "sudo dd of=/dev/sde status=progress bs=64k"
us 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 002: ID 2109:2812 VIA Labs, Inc. VL812 Hub
Bus 002 Device 003: ID 13d3:5741 IMC Networks Lenovo EasyCamera
Bus 002 Device 004: ID 105b:e065 Foxconn International, Inc. BCM43142A0 Bluetooth module
Bus 003 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 002: ID 2109:0812 VIA Labs, Inc. VL812 Hub
Bus 003 Device 004: ID 2537:1068 Norelsys NS1068/NS1068X SATA Bridge Controller
Bus 003 Device 005: ID 0bda:8153 Realtek Semiconductor Corp. RTL8153 Gigabit Ethernet Adapter
Bus 003 Device 006: ID 152d:0578 JMicron Technology Corp. / JMicron USA Technology Corp. JMS578 SATA 6Gb/s
You have two external SATA adapters connected. The one causing the UAS crash is the JMicron JMS578 (152d:0578) controller on Bus 003.
Step 1: Force usb-storage Driver for JMicron (152d:0578)
Unload the crashing uas module and reload usb-storage with the quirk flag for 152d:0578:
sudo modprobe -r uas
sudo modprobe usb-storage quirks=152d:0578:u
Step 2: Cycle the Connection
Step 3: Check Drive Assignment & Disable Auto-Mount
Step 4: Resume ddrescue
Run ddrescue with --no-scrape (-n) and a 4KiB block size (-b 4096). It will now report read errors to ddrescue instantly and skip ahead without crashing the USB adapter:
sudo ddrescue -n -b 4096 /dev/sda - | ssh -p 2010 remote_user@REMOTE-TAILSCALE-IP "sudo dd of=/dev/sde status=progress bs=64k"
(Replace /dev/sda if lsblk shows a different letter after re-plugging).
after dd is finished, how to know ?which files are damaged
To identify which specific files are damaged, you need a map of where the bad sectors are located on the disk, and then you cross-reference those bad sector addresses with your filesystem's inode table using testdisk or debugfs.
Because you piped ddrescue directly into dd over SSH, no local rescue.log mapfile was generated. However, you can still pinpoint corrupt files directly on the cloned target disk (/dev/sde).
Step 1: Scan the Target Disk for Bad Sectors
Run badblocks on the cloned target drive /dev/sde. Since /dev/sde now contains an exact sector-by-sector copy of /dev/sda, reading /dev/sde will reveal where unreadable/skipped zeros were written:
sudo badblocks -v -b 4096 /dev/sde > bad_sectors.txt
(Save the list of bad sector block addresses to bad_sectors.txt).
Step 2: Map Bad Sectors to Corrupted Files
Depending on your file system type (e.g., ext4 or NTFS), use the appropriate tool below to match those sector numbers to actual file paths.
Option A: For EXT4 Filesystems (Using debugfs)
Option B: Automated GUI Recovery (Using TestDisk)
TestDisk is much easier if you prefer an automated tool to scan and list damaged files across ext4, NTFS, or FAT32 filesystems:
Step 3: Run Filesystem Repair (e2fsck)
Before mounting /dev/sde for normal use, repair any filesystem metadata inconsistencies caused by bad sectors:
sudo e2fsck -f -y /dev/sde1
Basically , target disk has no bad sector physically, then badsector information from source disk affects on target disk ?
No, the target disk will not gain physical bad sectors.
Because /dev/sde is a healthy physical drive, its physical platters or NAND flash cells remain 100% fine. However, logical/file system corruption will be copied over.
Physical vs. Logical Bad Sectors
What Actually Happens to Target Disk (/dev/sde)
How to Fix the File System on Target Disk
Once ddrescue completes, run a file system check on /dev/sde. This cleans up the logical errors so the operating system can safely use the drive:
sudo e2fsck -f -y /dev/sde1
Can I know the list of damaged files?
Yes, you can generate a complete list of damaged files directly from the target disk (/dev/sde), even though it has no physical bad sectors of its own.
Because ddrescue writes all zeros to the target disk whenever it encounters an unreadable sector on the source drive, any file containing those zeroed-out blocks is corrupted.
Method 1: The Automated Way (Using ddrescue-tools)
If you want a direct list of damaged file paths, use ddrescue-view or ddrescue-tools to parse the cloned partition:
Method 2: Finding Corrupted Files manually on EXT4 (debugfs)
If you don't have a mapfile from ddrescue, you can scan /dev/sde1 for filesystem blocks that contain unreadable metadata or lost inodes:
Method 3: Finding Zeroed / Damaged Files by Checksum Verification
If you have a previous backup or known checksums (like .md5 or .sha256 files) for your media, run a verification scan over your mounted files:
find /mnt/target -type f -exec md5sum {} + > /tmp/checksums.txt 2> /tmp/corrupted_files.txt
Any file that fails to read or produces an error will be logged into /tmp/corrupted_files.txt.
Security
Brute attack and /var/log/btmp
**/var/log/btmp` is a system security log that records all failed login attempts to your Linux server.**/var/log/btmp- It is a binary file: You cannot read it using normal text commands like
cat,nano, orless. If you try, your screen will fill with unreadable scrambled code. - Strict Permissions: Because it logs failed login details (which sometimes accidentally contain real passwords typed into the username field), only the
rootuser can read it.
last with the -f flag. Run this command with sudo:sudo last -f /var/log/btmp
root ssh:notty 192.168.1.50 Thu Aug 27 17:45 - 17:45 (00:00)
admin ssh:notty 45.142.120.14 Thu Aug 27 17:42 - 17:42 (00:00)
invalid ssh:notty 185.220.101.5 Thu Aug 27 17:40 - 17:40 (00:00)
- Column 1: The username attempted (e.g.,
root,admin, or completely fake usernames likeinvalid). - Column 3: The IP address of the machine or bot trying to break into your server.
/var/log/btmp to swell to hundreds of megabytes or gigabytes very quickly.sudo last -f /var/log/btmp | wc -l
2010, sophisticated automated bots will eventually scan your server, find the open port, and begin brute-forcing it./var/log/btmp file and find out exactly who is attacking you and if Fail2ban is doing its job.btmp file using this command:sudo last -f /var/log/btmp | awk '{print $3}' | sort | uniq -c | sort -nr | head -n 10
- What this does: It extracts the IP column, counts the duplicate entries, and displays the top 10 most aggressive IP addresses alongside the exact number of failed attempts they made.
sudo last -f /var/log/btmp | awk '{print $1}' | sort | uniq -c | sort -nr | head -n 10
- What to look for: You will likely see
root,admin,user, orubuntu. If you see actual personal usernames that exist on your system, it means the attack is more targeted.
2010 means you need to ensure Fail2ban is actually listening to that custom port. Otherwise, it will ignore the attacks.sudo fail2ban-client status sshd
- Look at the "Banned IP list" at the bottom of the output. If it shows active IP addresses, Fail2ban is successfully protecting your new port.
- If the list is completely empty despite having thousands of entries in your
btmplog, Fail2ban might still be looking for port 22.
/etc/fail2ban/jail.local):[sshd]
enabled = true
port = 2010
logpath = %(sshd_log)s
backend = %(sshd_backend)s
port = 2010 is explicitly set. If you make changes, restart the service:sudo systemctl restart fail2ban
103.137.184.170 alone has hammered your custom SSH port over 216,000 times.Journal matches: _SYSTEMD_UNIT=sshd.service), has banned 427 IPs total, and currently has 7 IPs blocked—including 211.253.8.115, which is number 10 on your top attackers list.sudo nano /etc/fail2ban/jail.local
[sshd] section and adjust these parameters:[sshd]
enabled = true
port = 2010
maxretry = 3 ; Ban them after just 3 failed attempts
findtime = 1d ; Look for 3 failures within a 24-hour window
bantime = 1w ; Ban them for a full week (or use -1 for permanent)
Ctrl+O, Enter, Ctrl+X) and restart Fail2ban:sudo systemctl restart fail2ban
103.137.184.170 has broken the threshold completely. You can force Fail2ban to ban it instantly without waiting for the next trigger:sudo fail2ban-client set sshd banip 103.137.184.170
root user. If you haven't already, completely disable logging directly into the root account via SSH.sudo nano /etc/ssh/sshd_config
PermitRootLogin and change it to no:PermitRootLogin no
sudo systemctl restart ssh
meson user password or SSH key working before doing this, so you don't lock yourself out)./var/log/btmpsudo truncate -s 0 /var/log/btmp
- Do you log in using a password or an SSH Cryptographic Key? (Switching to keys stops brute forces completely).
- Would you like help configuring UFW (the firewall) to restrict SSH port 2010 access to only your specific home/office IP?
#PermitRootLogin prohibit-password is commented out, the system defaults to allowing root login attempts under certain conditions. Since you are using SSH keys for your user account, you should explicitly deny the root user from ever logging in.sudo nano /etc/ssh/sshd_config
# symbol in front of it):PermitRootLogin no
Ctrl+O, Enter, Ctrl+X) and restart SSH:sudo systemctl restart ssh
103.137.184.170, you should block it permanently at the firewall level so it cannot access your Nextcloud instance or any other port:sudo ufw insert 1 deny from 103.137.184.170 to any
insert 1 ensures this rule sits at the very top of your firewall checklist, blocking the packet instantly).sudo ufw delete allow 2010/tcp
sudo ufw allow from YOUR_HOME_IP to any port 2010 proto tcp
YOUR_HOME_IP with your actual public IP address, which you can find by searching "what is my IP" on your computer).Security against Brute attack
This volume of failed logins is normal background noise for any server with port 22 open to the internet — automated bots scan all IPv4 addresses and start hammering common usernames within minutes of your server going online. Your fail2ban and UFW are doing exactly what they're supposed to: stopping them from actually getting in.
That said, you can eliminate most of this traffic entirely. If you only have SSH keys enabled, the thousands of password guesses are harmless and simply never succeed.
Is this normal?
-
1564 failed logins / 192 bans / 7329 firewall blocks is a typical-to-calm level for a public server. Many freshly-unprotected boxes see tens of thousands of attempts daily.
-
The important test: do any of the failed logins succeed? If they don't (key-based auth only), this is nuisance traffic, not a breach.
-
One thing worth checking: if most failures come from a single IP, fail2ban may be misconfigured or its filter may not match your auth log format — otherwise it should be banning that IP. Verify with
sudo fail2ban-client status sshd.
What you can do to cut it down
The single most effective change is to make password login impossible, so the brute-force attempts can never succeed:
-
Use SSH keys only — this alone eliminates the overwhelming majority of brute-force risk.
-
Disable root login (
PermitRootLogin no) so attackers can't target root directly. -
Rate-limit at the firewall:
sudo ufw limit OpenSSH(allows ~6 connections per 30s per IP, dropping brute-force spikes at the kernel level). -
Use a non-standard port (e.g., 2222) — reduces scanner traffic dramatically, though it's security-by-obscurity.
-
Restrict access to known admin IPs or a VPN (Tailscale/WireGuard) if your IP is static — then port 22 isn't exposed at all.
Before you change anything
-
Confirm key-based login already works from a fresh session before disabling passwords, or you'll lock yourself out.
-
Keep one SSH session open, back up the config (
cp, thensshd -tto validate syntax), thensudo systemctl reload ssh— not restart, so existing sessions survive. -
Check that your cloud provider has a web/serial console as a break-glass fallback.
Bottom line: there's nothing special or alarming in your numbers — it's the standard pre-scan traffic every internet-exposed server receives. If you want to reduce it (and the log noise), the fixes are key-only auth + disable root + firewall rate limiting. Once that's in place, the 1564 failures are wasted effort against a door that's already locked.
Want me to check whether any of the failed logins actually succeeded on a real account, or walk through a specific hardening step?
Yes — you should update fail2ban so its ban action targets your port 2010. The good news: fail2ban detects attacks by parsing your auth log, not by watching ports, so it still sees the failed logins on any port. The problem is only the ban itself — if fail2ban's action still blocks port 22 (or the OpenSSH app profile that points to 22), the banned IPs aren't actually being blocked from connecting to 2010.
What to change
On Ubuntu with UFW, the cleanest way is a dedicated UFW application profile for port 2010, then point the sshd jail at it.
1. Add an app profile for your port in /etc/ufw/applications.d/openssh-server:
2. Point the sshd jail at it in /etc/fail2ban/jail.local:
3. Reload and verify:
The port = 2010 line tells fail2ban the port to report/ban, and the action = ufw[application="OpenSSH-2010"] makes UFW insert a deny from <ip> to any app OpenSSH-2010 rule that actually drops traffic to 2010.
Important caveat
There's a known failure mode: if you leave fail2ban's default iptables/ufw action pointing at 22 or forget to update it, fail2ban-client status sshd will show bans, but ufw status will have no matching deny rules and the IPs keep reaching 2010 — exactly the "fails to block ssh on custom port" problem.
Two checks to confirm it's working after changing the port:
-
sudo fail2ban-client status sshd— shows the banned IP count. -
sudo ufw status numbered— verify a matchingDENY ... to 2010/tcp(orto ... app OpenSSH-2010) rule exists for recently banned IPs.
If you'd rather not maintain a custom profile, the alternative is the simpler stock line action = ufw with banaction = ufw under [DEFAULT] — but that still binds to the port = 2010 you set, so make sure that's updated.
One extra tip since you moved to 2010: add a second jail watching for activity on port 22 too — legacy scanners may still probe it, and banning them at 22 keeps noise out of your UFW logs.
Yes — just append that block to the end of /etc/ufw/applications.d/openssh-server. Adding a second [OpenSSH-2010] section alongside the existing [OpenSSH] (port 22) profile is fine; UFW supports multiple profiles in one file and each is referenced by its own bracketed name.
Append at the bottom:
Two quick notes:
-
Don't add a second
[OpenSSH]with the same name, or UFW will complain about a duplicate app — a uniquely-named block is the correct approach. -
After saving, re-read the profile list and confirm it appears:
Then you can use it in boththe firewall rule and the fail2ban action:
If ufw app list doesn't show the new profile after saving, just re-run sudo ufw app update OpenSSH-2010 (or reload UFW) to refresh its parsed application definitions.
Performance
Mariadb Slow log
/var/log/mysql/mariadb-slow.log is the standard file path used to store the slow query log in MariaDB and MySQL databases on Linux systems. This log records any SQL queries that exceed a defined execution time threshold, helping database administrators diagnose and troubleshoot performance bottlenecks. [1, 2, 3, 4]SHOW VARIABLES LIKE 'slow_query_log%';
SHOW VARIABLES LIKE 'long_query_time';
slow_query_log: Should beONto record queries.slow_query_log_file: Path to the log file (e.g.,/var/log/mysql/mariadb-slow.log).long_query_time: The threshold in seconds. Queries taking longer than this value are logged. [1, 2, 3, 4]
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL slow_query_log_file = '/var/log/mysql/mariadb-slow.log';
SET GLOBAL long_query_time = 2; -- Logs queries taking over 2 seconds
SET GLOBAL log_queries_not_using_indexes = 'ON'; -- Optional: logs unindexed queries
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mariadb-slow.log
long_query_time = 2
log_queries_not_using_indexes = 1
sudo mkdir -p /var/log/mysql
sudo touch /var/log/mysql/mariadb-slow.log
sudo chown -R mysql:mysql /var/log/mysql
sudo chmod 750 /var/log/mysql
sudo systemctl restart mariadb
# Or on some systems: sudo systemctl restart mysql
cat or vi, use native parsing tools: [1, 2]- Using
mysqldumpslow: A built-in command-line tool that groups similar queries together and sorts them by execution time.bashmysqldumpslow -s t /var/log/mysql/mariadb-slow.logUse code with caution. - Using
pt-query-digest: Part of the advanced Percona Toolkit, this creates highly detailed and easily readable performance summaries.bashpt-query-digest /var/log/mysql/mariadb-slow.log[1, 2]Use code with caution.
- Is your database currently experiencing slow downs or high CPU usage?
- Do you need help optimizing a specific slow query found in this log?
- Are you getting a permission denied error when MariaDB tries to create the file?
-
mariadb.comSlow Query Log Overview | Server | MariaDB DocumentationSlow Query Log Overview. Complete Slow Query Log Overview gu...
-
PleskHow to enable the MySQL/MariaDB slow query log and analyze it on ...Answer * Connect to your Plesk server via SSH. * Enable the ...
-
cPanelHow to Enable the Slow Query Log in MySQL® or MariaDBTo enable the Slow Query Log for MySQL or MariaDB: * Log in ...
- Identifies slow pages: If a website page takes 5 seconds to load, this log reveals the exact SQL query causing the delay.
- Pins down high CPU/Memory: Queries that scan millions of rows without using indexes spike server resources. The log flags them immediately.
- Spotting unindexed tables: By enabling
log_queries_not_using_indexes, the log captures queries performing full table scans. - Easy fixes: Adding a single index to a column flagged in this log can often drop a query's execution time from 3 seconds to 3 milliseconds.
- Poorly written ORM queries: Modern web frameworks (like Laravel, Hibernate, or Django) sometimes generate massive, inefficient SQL joins behind the scenes.
- N+1 query problems: It reveals loops where your application accidentally runs hundreds of tiny, redundant queries instead of one efficient query.
- Tracking growth trends: By comparing log sizes and query counts month-over-month, you can see if your database performance is degrading as your user base grows.
- Postponing costly upgrades: Optimizing the top 3 slow queries in this log can often slash server resource usage by 50%, saving you from paying for a larger, more expensive server.
- Is your database currently running slow, or are you just setting this up proactively?
- What application or framework (e.g., WordPress, Node.js, Python) is connecting to this database?
- Would you like an example of how to read a raw entry from this log file?
mariadb-slow.log.1 is not normal for a healthy, optimized production database..1 extension indicates this is a rotated log file (an older log preserved by Linux logrotate), meaning your database generated 3 GB of slow query data in a single log rotation period—typically just one week or one day.- Missing Indexes (Most Common): If a query searches a table with 1 million rows without an index, MariaDB must read all 1 million rows from the disk every single time. This is called a "Full Table Scan."
log_queries_not_using_indexesis ON: If this setting is enabled, MariaDB logs every single query that doesn't use an index, even if it executes in 0.001 seconds. On a busy site, this will inflate the log file to gigabytes in hours.long_query_timeis set too low: If this is set to0or a very low fraction (like0.01seconds) on a high-traffic application, almost every single query gets logged.- An Application Loop Bug: Your application code might be trapped in a loop, running the same slow query thousands of times per minute.
cat, nano, or vi, as it can freeze your server's memory. Instead, look at just the first few lines:head -n 50 /var/log/mysql/mariadb-slow.log.1
SHOW VARIABLES LIKE 'log_queries_not_using_indexes';
SHOW VARIABLES LIKE 'long_query_time';
- If
log_queries_not_using_indexesisONand you have a busy database, turn it off to stop the log explosion:sqlSET GLOBAL log_queries_not_using_indexes = 'OFF';Use code with caution.
mysqldumpslow to extract the top 5 worst offending queries that are taking up all that space:mysqldumpslow -s c -t 5 /var/log/mysql/mariadb-slow.log.1
-s c) and shows the top 5 (-t 5) query patterns causing the bulk of the log size).sudo truncate -s 0 /var/log/mysql/mariadb-slow.log.1
- What is the output of the
mysqldumpslowcommand above? (You can paste a small snippet of the top query pattern). - What are your current
long_query_timeandlog_queries_not_using_indexesvalues? - Is your server currently running out of disk space because of this?
oc_filecache table). The log is exploding because log_queries_not_using_indexes is turned ON, combined with Nextcloud constantly checking its app data folder.Query_time: 0.089309: The query is actually very fast, taking only 0.08 seconds (well below your 2.0-secondlong_query_timethreshold).Full_scan: Yes: The query is doing a full table scan, checking all191,623rows in theoc_filecachetable every single time it runs.- Why it's logged: Because
log_queries_not_using_indexes = ON, MariaDB is forced to write this fast query to your log file every few hours. On a busy server with multiple users or cron jobs, this fills up gigabytes of text very quickly.
SET GLOBAL log_queries_not_using_indexes = 'OFF';
/etc/mysql/mariadb.conf.d/50-server.cnf or /etc/my.cnf) and change or add this line under the [mysqld] section:log_queries_not_using_indexes = 0
sudo truncate -s 0 /var/log/mysql/mariadb-slow.log.1
SELECT COUNT(*) FROM oc_filecache WHERE path LIKE 'appdata_...%' uses a wildcard (%) at the end of the string. Nextcloud should be using its built-in index on the path column, but sometimes these indexes get corrupted or missing during major Nextcloud upgrades./var/www/nextcloud or /var/www/html):sudo -u www-data php occ db:add-missing-indices
www-data with your web server user if you are using a different one like nginx or apache).- Did turning off
log_queries_not_using_indexessuccessfully stop the log from growing? - Do you know the exact directory path where your Nextcloud/OwnCloud instance is installed?
- Are you running this instance inside a Docker container, or directly on the host system?
Journal log size Opimization
sudo journalctl --vacuum-size=100M to limit the size of your system logs. [1]- Check current size: Run
journalctl --disk-usageto see how much space the logs take. - Reduce by size: Run
sudo journalctl --vacuum-size=500Mto shrink the logs down to 500 megabytes (or another size like1G). - Reduce by time: Run
sudo journalctl --vacuum-time=2weeksto delete logs older than a specific timeframe. [1, 2, 3]
- Open config file: Edit
/etc/systemd/journald.confusing a text editor with root privileges (likesudo nano /etc/systemd/journald.conf). - Set maximum limits: Find and uncomment (remove the
#at the start of) lines likeSystemMaxUse=500MorSystemMaxFileSize=50Mto
-
Linux MintSOLVED: var/log is large and keeps growing... /var/log/lightdm 4.0K /var/log/mintsystem.log 4.0K /var/
-
Debian forums[Solved] How to manage the size of /var/log/journalMy system runs headless and I have only 782 packages install...
-
SiteLintHow do I clear a big /var/log/journal folder? - SiteLintLet's then delete old log files from the /var/log/journal fo...
- Desktop / Personal PC (500MB – 1GB): Enough for daily troubleshooting without wasting SSD space.
- Standard Web/App Server (1GB – 2GB): Retains weeks of operational history for debugging.
- Production / High-Traffic Server (4GB – 10GB): Necessary if you log heavy application traffic locally.
- Containers / Microservices (50MB – 100MB): Keep it minimal since logs should stream to an external aggregator.
- Disk Capacity: A safe rule of thumb is to limit your journal to 10% of your total partition size (systemd's default behavior) or caps at 4GB, whichever is smaller.
- Log Rotation Frequency: If your system generates 500MB of logs a day, a 1GB limit only gives you 2 days of history.
- External Forwarding: If you send logs to a central server (like Elasticsearch or Datadog), you can safely set the local journal size much lower.
- Is this for a personal PC or a server?
- What is the total storage capacity of your drive?
sudo nano /etc/systemd/journald.confSystemMaxUse=2G(Caps total disk space used by logs)SystemMaxFileSize=200M(Splits logs into smaller, easier-to-read files)RuntimeMaxUse=200M(Caps log memory usage to protect your 8GB RAM)
sudo systemctl restart systemd-journaldEssential logs to watch
For an Ubuntu server administrator, I recommend monitoring logs in six major areas: kernel/hardware, storage, system services, security, networking, and applications.
1. System and Kernel Logs — highest priority
Kernel messages
journalctl -k
dmesg -T
Watch for:
I/O errorEXT4-fs errorBuffer I/O errorresetdisconnectUSB errorOut of memoryOOM killersegfaulthardware errorthermalwatchdog
For live monitoring:
journalctl -kf
2. Storage and Disk Health ⚠️
Especially important for servers running databases, Docker, Nextcloud, Immich, etc.
Filesystem errors
journalctl -k | grep -iE "error|ext4|xfs|btrfs|i/o"
Critical messages include:
EXT4-fs error
Buffer I/O error
blk_update_request
I/O error
device offline
reset
Disk SMART health
sudo smartctl -a /dev/sda
Monitor:
| SMART Attribute | Concern |
|---|---|
| Reallocated_Sector_Ct | Growing |
| Current_Pending_Sector | > 0 |
| Offline_Uncorrectable | > 0 |
| UDMA_CRC_Error_Count | Increasing |
| Temperature | High |
| Power_On_Hours | Aging disks |
Run long tests periodically:
sudo smartctl -t long /dev/sda
3. Memory and OOM Events
OOM killer
journalctl | grep -iE "out of memory|oom|killed process"
Live:
journalctl -f | grep -iE "oom|memory"
Check memory:
free -h
Swap:
swapon --show
For servers, unexpected OOM events are high priority because they may silently kill:
- MariaDB
- PHP-FPM
- Redis
- Docker containers
- Nextcloud background processes
4. Failed System Services
One of the most useful administrator commands:
systemctl --failed
Check system errors:
journalctl -p err..alert
Recent boot errors:
journalctl -b -p warning
Useful services to watch:
systemctl status nginx
systemctl status php8.3-fpm
systemctl status mariadb
systemctl status redis-server
systemctl status docker
Depending on your PHP version:
systemctl list-units --type=service
5. Nginx Web Server
For web servers:
tail -f /var/log/nginx/error.log
Access log:
tail -f /var/log/nginx/access.log
Important errors:
502 Bad Gateway
504 Gateway Timeout
connect() failed
upstream timed out
permission denied
too many open files
For error frequency:
grep -i "error" /var/log/nginx/error.log
6. PHP-FPM
Check:
journalctl -u php8.3-fpm
Live:
journalctl -fu php8.3-fpm
Watch for:
- PHP worker crashes
server reached pm.max_children- Out of memory
- Slow requests
- Permission errors
- Session errors
A particularly important message:
server reached pm.max_children
This usually means PHP-FPM is saturated.
7. MariaDB / MySQL Database
Check logs:
journalctl -u mariadb
Live:
journalctl -fu mariadb
Watch for:
- InnoDB corruption
- Disk full
- Connection exhaustion
- Crashes
- Table corruption
Useful checks:
mysqladmin status
Connections:
mysqladmin processlist
8. Docker
For your type of server setup, Docker should be monitored continuously.
Docker daemon
journalctl -u docker
Live:
journalctl -fu docker
Container status
docker ps
Stopped containers:
docker ps -a
Container logs:
docker logs CONTAINER_NAME
Live:
docker logs -f CONTAINER_NAME
Check Docker disk usage:
docker system df
This is important because Docker logs and images can unexpectedly fill the filesystem.
9. Disk Space 🚨
Run:
df -h
Also check inode exhaustion:
df -ih
Administrators should generally investigate:
| Usage | Status |
|---|---|
| < 70% | Normal |
| 70–85% | Monitor |
| 85–95% | Warning |
| >95% | Critical |
Find large directories:
sudo du -xh / --max-depth=1 2>/dev/null | sort -h
For logs:
journalctl --disk-usage
Reduce old journals if necessary:
sudo journalctl --vacuum-time=14d
10. Network Problems
Check networking services:
journalctl -u NetworkManager
For Ubuntu Server using systemd-networkd:
journalctl -u systemd-networkd
Current network connections:
ss -tulpn
Network errors:
ip -s link
Watch for:
- RX errors
- TX errors
- Dropped packets
- Interface resets
11. SSH and Security 🔐
Ubuntu authentication log:
sudo tail -f /var/log/auth.log
Failed SSH attempts:
grep "Failed password" /var/log/auth.log
Successful logins:
grep "Accepted" /var/log/auth.log
Current users:
who
Recent logins:
last
Failed login database:
lastb
12. System Reboots and Unexpected Shutdowns
Check reboot history:
last reboot
Previous boot:
journalctl -b -1
Check for unexpected shutdown:
journalctl | grep -iE "shutdown|reboot|panic|crash"
Kernel panic:
journalctl -k | grep -i panic
13. Cron and Scheduled Jobs
Depending on Ubuntu configuration:
journalctl -u cron
Or:
grep CRON /var/log/syslog
Important for:
- Nextcloud cron
- Backups
- Database dumps
- SSL certificate renewal
- Monitoring scripts
14. Nextcloud-specific Monitoring
For your Nextcloud servers, I would specifically watch:
sudo -u www-data php /var/www/nextcloud/occ status
Cron:
sudo -u www-data php /var/www/nextcloud/occ background:cron
Logs:
tail -f /path/to/nextcloud/data/nextcloud.log
Watch for:
- Database errors
- Redis connection failures
- File locking problems
- PHP memory exhaustion
- Background job failures
- WebDAV errors
- WOPI/Collabora errors
⭐ My Recommended Administrator Dashboard
For a self-hosted Ubuntu server, these are the most important things to continuously monitor:
┌───────────────────────────────────────┐
│ Ubuntu Server Monitoring │
├───────────────────────────────────────┤
│ 🔴 Disk I/O / Filesystem errors │
│ 🔴 SMART disk health │
│ 🔴 Disk space / inode usage │
│ 🔴 OOM / memory exhaustion │
│ 🔴 Failed systemd services │
│ 🔴 Docker container failures │
│ 🟠 Nginx 502/504 errors │
│ 🟠 PHP-FPM saturation │
│ 🟠 MariaDB errors │
│ 🟠 Network interface errors │
│ 🟠 SSH/security attacks │
│ 🟡 Backup failures │
│ 🟡 SSL certificate expiration │
│ 🟡 Temperature / hardware issues │
└───────────────────────────────────────┘
🚀 My recommended daily commands
# Critical system errors
journalctl -p err..alert --since today
# Failed services
systemctl --failed
# Disk usage
df -h
# Memory
free -h
# Docker containers
docker ps -a
# Kernel/hardware errors
journalctl -k -p err..alert
# Recent authentication failures
grep "Failed password" /var/log/auth.log | tail
For your Ubuntu servers specifically
Given that you operate multiple Ubuntu servers with Nextcloud, Nginx, PHP-FPM, MariaDB, Redis, Docker, Immich, and other self-hosted services, I would prioritize:
1. Disk I/O errors → 2. SMART health → 3. OOM events → 4. disk capacity → 5. failed services → 6. Docker container health → 7. Nginx/PHP/MariaDB errors.