megacolorboy

Abdush Shakoor's Weblog

Writings, experiments & ideas.

Enable password asterisks for sudo on Fedora

After switching from Linux Mint to Fedora, one small difference I noticed was the password prompt behavior in the terminal. On Mint, sudo showed asterisks while typing the password. On Fedora, it stayed blank.

Some people prefer the blank prompt, but I like having visual feedback when typing, especially if I am not fully sure whether I missed a key.

If you want the same behavior on Fedora, you can enable sudo password feedback with a small sudoers change.

This note applies to sudo prompts in the terminal, not graphical login screens.

1. Open the sudoers file safely

Do not edit /etc/sudoers directly with a normal text editor. Use visudo so syntax errors are caught before the file is saved:

sudo visudo

2. Enable password feedback

Add this line:

Defaults pwfeedback

If you already have other Defaults lines such as Defaults env_reset, leave them as they are. Just add Defaults pwfeedback as a separate line.

3. Save and test

Save the file and exit the editor. Then run a sudo command again, for example:

sudo -k
sudo true

When prompted for your password, you should now see asterisks as you type. This setting makes password length visible on screen. If that matters in your environment, leave the default behavior in place.

Hope you found this article useful!

Clean removal of Linux from a Windows dual-boot machine

Recently, I purchased a ThinkPad X1 Carbon Gen 13 to replace my old Dell XPS 13 9360. It came with Windows 11, and instead of wiping it, I decided to set up a dual-boot system with Linux. I still need Windows for .NET development, but I use Linux most of the time.

I eventually settled on Fedora, but I tried several distributions and desktop environments before getting there. Each time I removed Linux, I needed to clean up the old partitions and boot entries properly so they would not interfere with the next installation.

If you are doing the same kind of distro-hopping on a UEFI system, this is the cleanup process that worked for me.

This note covers UEFI systems only.

If you want to remove Linux from a Windows dual-boot setup, there are two things to clean up:

  1. Delete the Linux partitions.
  2. Remove the Linux bootloader files and firmware entries.

Before making changes, make sure Windows is working normally and back up anything you want to keep from your Linux installation.

1. Delete the Linux partitions

Open Windows Disk Management and identify the partitions that belong to Linux. These are usually the Linux root partition, swap partition, and optionally a separate /home partition.

Do not delete:

  • The Windows partition
  • The EFI System Partition
  • The Windows recovery partition

Delete only the Linux partitions, then either leave the space unallocated or extend your Windows partition into it.

2. Remove Linux bootloader files from the EFI partition

Open Command Prompt as Administrator and assign a drive letter to the EFI System Partition:

diskpart
list vol
select vol <EFI volume number>
assign letter=S
exit

Then remove the Linux bootloader directories from the EFI partition:

S:
cd EFI
dir
rmdir /s /q <linux-folder>

Only delete the directory that matches your Linux installation. Common names include fedora, ubuntu, debian, and similar distro-specific folders.

Do not delete Microsoft.

3. Remove stale UEFI firmware boot entries

List the firmware boot entries:

bcdedit /enum firmware

Look for entries that reference your removed Linux installation, such as descriptions containing Fedora, ubuntu, GRUB, or a path that points to the distro's EFI loader.

Delete only the matching Linux entry:

bcdedit /delete {xxxx-xxxx-xxxx-xxxx}

Replace {xxxx-xxxx-xxxx-xxxx} with the actual identifier from the previous command.

4. Reboot and verify

Reboot the machine and confirm that the Linux boot entry is gone from the firmware boot menu and that Windows starts normally.

Hope you found this article useful!

Why Hangfire recurring jobs should always have stable IDs?

Recently, I learned that not giving explicit IDs to Hangfire recurring jobs can silently create duplicates.

When you call RecurringJob.AddOrUpdate(...) without a pre-defined ID, Hangfire auto-generates one based on the method expression. That sounds fine—until you deploy across multiple nodes or refactor your code.

In such cases, Hangfire may generate a new derived ID, while the old recurring job continues running in the background. The result? Duplicate jobs executing on schedule, often goes unnoticed.

Simple fix

Make sure that you always pass a clear, human-readable ID:

RecurringJob.AddOrUpdate<ICustomBackgroundServiceManager>(
    "payments:reconcile-3h", // Human-readable ID
    s => s.ResendPaymentReportToVendorHourly(),
    Cron.Hourly(3)
);

Next, ensure that overlaps are impossible in a multi-node setup by adding this attribute to your job method:

[DisableConcurrentExecution(timeoutInSeconds: 3600)]
public Task ResendPaymentReportToVendorHourly() { /* ... */ }

This uses a distributed lock backed by Hangfire storage—so even with multiple nodes, only one execution runs at a time. If you’re wondering whether this locks the job for the full hour: it doesn’t. The timeout exists for crash-safety, not throttling.

After doing this, recurring jobs become much easier to reason about, identify, pause, or delete in a multi-node environment.

Hope you found this tip useful!

Running Laravel queues with Supervisor on Ubuntu

Laravel queues aren’t new to me, but I realized I’d never really written down how I usually set them up on a fresh Ubuntu server.

Whenever I need queue workers to run continuously — and survive restarts or crashes — I almost always reach for Supervisor. It’s simple, boring, and gets the job done.

First, install it on your server:

sudo apt update
sudo apt install supervisor

Then create a small config file for the queue worker:

sudo vim /etc/supervisor/conf.d/laravel-worker.conf

This is the setup I generally start with:

[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/project/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/project/storage/logs/laravel-worker.log
stopwaitsecs=600

A few quick notes on what this does:

  • It runs queue:work instead of queue:listen, so the worker stays in memory
  • Two worker processes are started in parallel (numprocs=2)
  • If the queue is empty, the worker sleeps for a few seconds instead of spinning
  • Failed jobs are retried a limited number of times
  • The worker is force-restarted every hour to avoid memory leaks
  • If a worker crashes, Supervisor brings it back up automatically

Once the file is saved, reload Supervisor’s config:

sudo supervisorctl reread
sudo supervisorctl update

Start the workers:

sudo supervisorctl start laravel-worker:*

And check their status:

sudo supervisorctl status

Whenever I deploy new code or change environment variables, I just restart them:

sudo supervisorctl restart laravel-worker:*

Logs end up in Laravel’s storage/logs, which is usually the first place I look when something feels off.

This setup has been reliable for me across multiple projects. No dashboards, no extra moving parts — just queue workers quietly doing their job in the background.

Hope you found this useful!

DateTimeKind.Unspecified can quietly break your dates

Two months ago, a client raised a critical ticket where some users complained that their Start Date of the Financial Year had gone one day backward. This wasn’t caught during UAT while integrating their API, and I was surprised to run into this date conversion bug while I was on vacation (yes, that sucks!).

I had a perfectly normal-looking date:

2025-01-01 00:00:00.0000000

Nothing fancy. But after converting it to UTC, I noticed something odd — the date changed.

Luckily, I was able to trace where it was coming from, and after debugging in Visual Studio 2022, it turned out the culprit was DateTimeKind.Unspecified.

When a DateTime is parsed without timezone information, .NET marks it as Unspecified. If you then call ToUniversalTime(), .NET assumes the value is local time and converts it to UTC. On a UTC+5:30 system, that means:

2025-01-01 00:00 → 2024-12-31T18:30:00Z

Same instant, different calendar date. Easy to miss, painful to debug.

The fix

If your date is already meant to be UTC, you need to say so explicitly:

DateTime.SpecifyKind(inputDateTime, DateTimeKind.Utc);

Or handle it defensively in one place:

switch (inputDateTime.Kind)
{
    case DateTimeKind.Utc:
        return inputDateTime;

    case DateTimeKind.Local:
        return inputDateTime.ToUniversalTime();

    case DateTimeKind.Unspecified:
    default:
        return DateTime.SpecifyKind(inputDateTime, DateTimeKind.Utc);
}

Takeaway

Never leave DateTimeKind to chance especially if you’re working with APIs, audits, or anything date-sensitive.

It’s one of those small details that only shows up when things go wrong — which makes it worth handling upfront.

Hope you found this tip useful!

Resize images from a file list using ImageMagick

Today, a colleague of mine uploaded a large number of images, only to realise later that they were all uncompressed and the site was loading noticeably slower.

His first thought was to compress them and re-upload everything again—but why do something redundant when you can handle it easily from the terminal using ImageMagick?

Previously, I’ve written about how ImageMagick makes it easy to resize images in bulk. However, sometimes you don’t want to touch every image in a directory.

In cases like this, if you already know which images need resizing, you can list their filenames in a text file and let ImageMagick process only those.

Assume a files.txt like this:

image1.jpg
photo_02.png
banner.jpeg

You can then resize just those images while keeping the original aspect ratio intact and avoiding upscaling:

while IFS= read -r file; do
  [ -f "$file" ] && mogrify -resize 500x600\> "$file"
done < files.txt

This works well when cleaning up large media folders or fixing legacy content where only a subset of images needs adjustment.

Hope you found this tip useful!

Block browser features with permissions policy in Nginx

Recently, I learned that you can explicitly disable browser features like camera, microphone, and geolocation using the Permissions-Policy HTTP response header.

Using a single line in nginx, it does the job:

add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

What this does?

  • Disables camera access
  • Disables microphone access
  • Disables geolocation access
  • Applies to all origins
  • The browser won’t even prompt the user for permission

The empty () means no origins are allowed to use these features.

Why this is useful?

  • Improves security and privacy
  • Prevents misuse by third-party scripts
  • Good default for content sites, admin panels, and APIs

The always flag ensures the header is sent even on error responses (404, 500, etc.).

Hope you found this tip useful!

Ubuntu Bootloader Recovery

This is a guide on how to recover Ubuntu's GRUB Bootloader in the case of whenever a LVM UUID is changed or corrupted.

The Problem

The error "disk not found: /xxx-xxx" in grub rescue> mode suggests GRUB can't find the device or logical volume it was previously configured to boot from.

This usually happens when: * LVM volumes weren't activated during boot * The volume group UUID changed or became corrupted

The Context

The /xxx-xxx in the message is likely referring to a LVM volume by UUID, e.g.:

error: disk 'lvmid/XXX-XXX-XXX' not found

This typically means GRUB is referencing a missing or renamed LVM LV or VG

The Solution

Boot from a live ISO, open a terminal and follow these steps:

1. Check Disks and LVM State

sudo lsblk
sudo fdisk -l

Then:

sudo vgscan

This activates any found LVM volume groups and logical volumes.

Then verify with:

sudo lvdisplay

3. Mount the System Manually

Assuming your root volume is /dev/mapper/your_lvm_drive:

sudo mkdir /mnt/recovery
sudo mount /dev/mapper/your_lvm_drive /mnt/recovery

4. Prepare for chroot

sudo mount --bind /dev /mnt/recovery/dev
sudo mount --bind /proc /mnt/recovery/proc
sudo mount --bind /sys /mnt/recovery/sys
sudo chroot /mnt/recovery

The chroot command changes the root directory for the kernel, effectively making a specified directory the new starting point for any file access within the chrooted process.

5. Reinstall GRUB

grub-install /dev/sdX  # Replace sdX with the actual disk (like /dev/sda)
update-grub

Exit chroot mode, unmount the recovery path from live-boot OS and reboot the system:

exit
sudo umount -R /mnt/recovery
sudo reboot

Hope you found this article useful!