Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
149844 stories
·
33 followers

Microsoft releases PowerToys v0.96.1 with a nice surprise for Windows 10 users

1 Share
The question we ask every time there is a new PowerToys release is whether or not there are any new modules or options to play with. With the release of PowerToys v0.96.1 being a very minor update in terms of version number, there is a nice surprise in store. Sadly, if you are hoping for a brand new module, you are going to have to wait a little longer. We know of several utilities that are in the pipeline, but they are not ready yet. What we do see in this small update, however, is a nice surprise for Windows… [Continue Reading]
Read the whole story
alvinashcraft
48 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

How to Create an MSI Installer with Visual Studio 2026

1 Share
Visual Studio is a complete development environment for writing, testing, and deploying applications across different platforms. Beyond coding, it also supports creating MSI installers directly within the IDE. [...]
Read the whole story
alvinashcraft
48 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

How to install RSAT on Windows 11

1 Share
If you’re an IT administrator managing multiple Windows servers, you may have heard of Remote Server Administration Tools (RSAT). RSAT is a collection of Microsoft utilities that let you manage Windows Server roles and features remotely from a Windows client machine — meaning you don’t need to log in to the server itself to perform administrative tasks. [...]
Read the whole story
alvinashcraft
49 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Cloud-init on Raspberry Pi OS

1 Share

As some of you may have already noticed, the latest Raspberry Pi OS release based on Debian Trixie now includes cloud-init. This marks the beginning of a transition away from our legacy first-boot customisation system based on the firstrun.sh script.

Cloud-init is a cross-platform, distribution-agnostic tool used to automatically configure systems on first boot. (Definition adapted from the official cloud-init documentation.) With it, you can provision your Raspberry Pi images with users, network settings, SSH keys, storage configurations, and much more — all without manually logging in after flashing the image.

How can I use it?

If you’ve downloaded the latest image (released on 2 October 2025), you’ll find that three new files have appeared on the boot partition. This is the FAT32 partition that your computer automatically mounts when you insert a freshly flashed Raspberry Pi OS microSD card. It already contains familiar files like config.txt, but now you’ll also see:

  • meta-data
  • network-config
  • user-data

For most users, meta-data can be left untouched — it simply enables cloud-init to process the other configuration files correctly. Advanced users may use it for more complex provisioning workflows, but it’s safe to ignore in typical setups.

Cloud-init uses YAML for its configuration files. If you’re new to YAML, it’s worth taking a quick look at the official documentation, as indentation and formatting matter. For now, we’ll focus on the two most relevant files: user-data and network-config.

General configuration (user-data)

The user-data file is the central place for your configuration. With the exception of networking, almost everything cloud-init sets up on first boot is controlled from here.

You can use it to create a default user, define your locale, install additional packages, configure SSH access, and much more — all of which is covered in the official cloud-init documentation.

Unlike many other distributions, Raspberry Pi OS includes a few Raspberry Pi–specific extensions for cloud-init configuration. These allow you to enable hardware interfaces such as I2C, SPI, serial, and 1-Wire, and even activate USB gadget mode (rpi-usb-gadget) automatically.

Here’s an example configuration that sets up a user and demonstrates all the currently supported Raspberry Pi–specific options:

#cloud-config

# Set the hostname for this device. This will also update /etc/hosts if manage_etc_hosts is enabled.
hostname: mypi2025
manage_etc_hosts: true

# Set the system timezone
timezone: Europe/London

# Create a default user account and apply permissions
users:
  - name: pi
    groups: users,adm,dialout,audio,netdev,video,plugdev,cdrom,games,input,gpio,spi,i2c,render,sudo
    shell: /bin/bash
    lock_passwd: false  # Set to true to disable password login entirely
    plain_text_password: mysecretpassword123  # Alternatively, use 'passwd:' with a hashed password for better security
    ssh_authorized_keys:
      - ssh-ed25519 mykeystuff  # Replace with your actual SSH public key
    sudo: ALL=(ALL) NOPASSWD:ALL  # Allow passwordless sudo for this user

# Raspberry Pi–specific options (provided by the cc_raspberry_pi module)
rpi:
    spi: true               # Enable SPI interface
    i2c: true               # Enable I2C interface
    serial: true            # Enable serial console and UART interface
    onewire: true           # Enable 1-Wire interface
    enable_usb_gadget: true # Enable USB gadget mode

# Additional Raspberry Pi OS option (not available on generic cloud-init images)
enable_ssh: true  # Enables the SSH server on first boot

# Optional: Disable SSH password authentication if using SSH keys only (recommended for security)
# ssh_pwauth: false

For more details, you can refer to the cc_raspberry_pi module in the official cloud-init documentation.

Note:
The #cloud-config header at the top of the file is mandatory — cloud-init will not process the file correctly without it.

Networking configuration (network-config)

The network-config file defines how your Raspberry Pi should set up its network interfaces on first boot. As the name suggests, this is where you configure Wi-Fi or Ethernet settings before ever powering on the device.

Here’s a simple example that connects your Raspberry Pi to a Wi-Fi network:

network:
  version: 2
  wifis:
    # Make sure the target is NetworkManager which is the default on Raspberry Pi OS
    renderer: NetworkManager
    # The connection name
    wlan0:
      dhcp4: true
      # !VERY IMPORTANT! Change this to the ISO/IEC 3166 country code for the country you want to use this microSD card in.
      regulatory-domain: "GB"
      access-points:
        "My Net-Work":
          password: "mysupersecretpassword"
      # Don’t wait at boot for this connection to connect successfully
      optional: true

When you power on your Raspberry Pi with this microSD card inserted, cloud-init will process this configuration and attempt to connect to the specified network automatically — allowing you to SSH in or continue working without needing to attach a screen or a keyboard.

You can configure far more than just basic Wi-Fi credentials: multiple networks, priority fallback, static IP assignments, VLANs, and more are supported. For a full reference, see the official cloud-init networking documentation.

With the introduction of cloud-init, Raspberry Pi OS also includes Netplan, a unified abstraction layer for network configuration used by several modern Linux distributions.

More about Netplan

Netplan is now the primary source of truth for networking on Raspberry Pi OS. It uses its own YAML-based configuration format and can render network settings for both systemd-networkd and NetworkManager, depending on which renderer you choose. The major advantage of this approach is portability — a Netplan configuration can be reused across any Linux distribution that supports it, regardless of whether it uses NetworkManager or networkd underneath.

To use Netplan directly, place your configuration files in /etc/netplan/ — this is also where cloud-init stores your generated network configuration from network-config without modification. From there, you can generate the active configuration using:

sudo netplan generate

This writes the appropriate configuration files for the selected backend (NetworkManager on Raspberry Pi OS). To activate the configuration, run:

sudo netplan apply

You can still use nmcli as usual to inspect or manage connections. Since many existing tools and scripts rely on nmcli or the NetworkManager D-Bus API, there needs to be a communication layer between Netplan and NetworkManager. Canonical provides three patches that enable this two-way interoperability, allowing NetworkManager to signal configuration changes back to Netplan.

For Raspberry Pi OS, we’ve gone a step further and introduced additional patches to improve this workflow:

  • NetworkManager will only interact with connections that use the netplan- prefix. If you want a new connection to be persisted by Netplan, give it that prefix when creating it, and it will be stored in Netplan’s configuration.
  • When generating profiles, NetworkManager loads all Netplan-defined connections and writes its interpreted runtime configuration back in a format Netplan can understand.
  • During this process, all .yaml and .yml files under /etc/netplan/ are cleared to avoid conflicting definitions across multiple layers.
  • This does not affect manually created connections stored in /etc/NetworkManager/system-connections/.
  • Any profiles generated at runtime by Netplan will appear under /run/NetworkManager/system-connections/ and will also use the netplan- prefix.

This approach ensures consistency between both systems and prevents configuration drift when editing or reloading profiles via NetworkManager tools.

Wrapping up

With cloud-init and Netplan now integrated into Raspberry Pi OS, first-boot provisioning becomes far more powerful, repeatable, and portable across different setups. Whether you’re configuring a single device or preparing dozens of Raspberry Pis for a classroom, a lab, or an IoT deployment, these new tools make it easy to define everything up front — users, networking, interfaces, and more — before the system even powers on.

With the release of Raspberry Pi Imager 2.0, cloud-init configuration for Raspberry Pi OS is now generated by default. This makes it easy to further customise your setup after writing the image — simply edit the generated user-data or network-config files on the boot partition. Imager 2.0 also understands the Raspberry Pi–specific rpi: options, so features like SPI or I2C can be enabled directly in the customisation UI.

The legacy method still works, but cloud-init and Netplan open the door to a much more flexible and modern workflow. We’ll continue expanding support for Raspberry Pi–specific cloud-init modules and streamlined provisioning features in future releases.

If you create interesting user-data or network-config templates, or have feedback about the new system, we’d love to hear from you in the forums.


Header image resources: Background designed by Freepik. Icons by ziadarts, Dimas Anom, and Gregor Cresnar via Flaticon.

The post Cloud-init on Raspberry Pi OS appeared first on Raspberry Pi.

Read the whole story
alvinashcraft
49 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Background tasks in .NET

1 Share

What is a Background Task?
 

background task (or background service) is work that runs behind the scenes in an application without blocking the main user flow and often without direct user interaction.

Think of it as a worker or helper that performs tasks independently while the main app continues doing other things.

Problem Statement - 
What do you do when your downstream API is flaky or sometimes down for hours or even days , yet your UI and main API must stay responsive?
Solution -
This is a very common architecture problem in enterprise systems, and .NET gives us excellent tools to solve it cleanly: BackgroundService and exponential backoff retry logic.

In this article, I’ll walk you through:

  • A real production-like use case
  • The architecture needed to make it reliable
  • Why exponential backoff matters
  • How to build a robust BackgroundService
  • A full working code example

The Use Case

You have two APIs:

  • API 1 :  called frequently by the UI (hundreds or thousands of times).
  • API 2 : a downstream system you must call, but it is known to be
    unstable, slow, or completely offline for long periods.

If API 1 directly calls API 2:
*  Users experience lag
*  API 1 becomes slow or unusable
*  You overload API 2 with retries
*  Calls fail when API 2 is offline
*  You lose data

What do we do then? Here goes the solution
The Architecture
Instead of calling API 2 synchronously, API 1 simply stores the intended call, and returns immediately.

A BackgroundService will later:

  1. Poll for pending jobs
  2. Call API 2
  3. Retry with exponential backoff if API 2 is still unavailable
  4. Mark jobs as completed when successful

This creates a resilient, smooth, non-blocking system.

Why Exponential Backoff?

When a downstream API is completely offline, retrying every 1–5 seconds is disastrous:

  • It wastes CPU and bandwidth
  • It floods logs
  • It overloads API 2 when it comes back online
  • It burns resources

Exponential backoff solves this.

Examples retry delays:

Retry 1 → 2 sec
Retry 2 → 4 sec
Retry 3 → 8 sec
Retry 4 → 16 sec
Retry 5 → 32 sec
Retry 6 → 64 sec (Max delay capped at 5 minutes)


This gives the system room to breathe.


Complete Working Example (Using In-Memory Store)

1. The Model

public class PendingJob { public Guid Id { get; set; } = Guid.NewGuid(); public string Payload { get; set; } = string.Empty; public int RetryCount { get; set; } = 0; public DateTime NextRetryTime { get; set; } = DateTime.UtcNow; public bool Completed { get; set; } = false; }

 

2. The In-Memory Store

public interface IPendingJobStore { Task AddJobAsync(string payload); Task<List<PendingJob>> GetExecutableJobsAsync(); Task MarkJobAsCompletedAsync(Guid jobId); Task UpdateJobAsync(PendingJob job); } public class InMemoryPendingJobStore : IPendingJobStore { private readonly List<PendingJob> _jobs = new(); private readonly object _lock = new(); public Task AddJobAsync(string payload) { lock (_lock) { _jobs.Add(new PendingJob { Payload = payload, RetryCount = 0, NextRetryTime = DateTime.UtcNow }); } return Task.CompletedTask; } public Task<List<PendingJob>> GetExecutableJobsAsync() { lock (_lock) { return Task.FromResult(_jobs.Where(j => !j.Completed && j.NextRetryTime <= DateTime.UtcNow).ToList()); } } public Task MarkJobAsCompletedAsync(Guid jobId) { lock (_lock) { var job = _jobs.FirstOrDefault(j => j.Id == jobId); if (job != null) job.Completed = true; } return Task.CompletedTask; } public Task UpdateJobAsync(PendingJob job) => Task.CompletedTask; }

3. The BackgroundService with Exponential Backoff

using System.Text; public class Api2RetryService : BackgroundService { private readonly IHttpClientFactory _clientFactory; private readonly IPendingJobStore _store; private readonly ILogger<Api2RetryService> _logger; public Api2RetryService(IHttpClientFactory clientFactory, IPendingJobStore store, ILogger<Api2RetryService> logger) { _clientFactory = clientFactory; _store = store; _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("Background retry service started."); while (!stoppingToken.IsCancellationRequested) { var jobs = await _store.GetExecutableJobsAsync(); foreach (var job in jobs) { var client = _clientFactory.CreateClient("api2"); try { var response = await client.PostAsync("/simulate", new StringContent(job.Payload, Encoding.UTF8, "application/json"), stoppingToken); if (response.IsSuccessStatusCode) { _logger.LogInformation("Job {JobId} processed successfully.", job.Id); await _store.MarkJobAsCompletedAsync(job.Id); } else { await HandleFailure(job); } } catch (Exception ex) { _logger.LogError(ex, "Error calling API 2."); await HandleFailure(job); } } await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken); } } private async Task HandleFailure(PendingJob job) { job.RetryCount++; var delay = CalculateBackoff(job.RetryCount); job.NextRetryTime = DateTime.UtcNow.Add(delay); await _store.UpdateJobAsync(job); _logger.LogWarning("Retrying job {JobId} in {Delay}. RetryCount={RetryCount}", job.Id, delay, job.RetryCount); } private TimeSpan CalculateBackoff(int retryCount) { var seconds = Math.Pow(2, retryCount); var maxSeconds = TimeSpan.FromMinutes(5).TotalSeconds; return TimeSpan.FromSeconds(Math.Min(seconds, maxSeconds)); } }

4. The API 1 — Public Endpoint

using System.Runtime.InteropServices; using System.Text.Json; [ApiController] [Route("api1")] public class Api1Controller : ControllerBase { private readonly IPendingJobStore _store; private readonly ILogger<Api1Controller> _logger; public Api1Controller(IPendingJobStore store, ILogger<Api1Controller> logger) { _store = store; _logger = logger; } [HttpPost("process")] public async Task<IActionResult> Process([FromBody] object data) { var payload = JsonSerializer.Serialize(data); await _store.AddJobAsync(payload); _logger.LogInformation("Stored job for background processing."); return Ok("Request received. Will process when API 2 recovers."); } }

5. The API 2 (Simulating Downtime)

using System.Runtime.InteropServices; [ApiController][Route("api2")] public class Api2Controller: ControllerBase { private static bool shouldFail = true; [HttpPost("simulate")] public IActionResult Simulate([FromBody] object payload) { if (shouldFail) return StatusCode(503, "API 2 is down"); return Ok("API 2 processed payload"); } [HttpPost("toggle")] public IActionResult Toggle() { shouldFail = !shouldFail; return Ok($"API 2 failure mode = {shouldFail}"); } }

6. The Program.cs

var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); builder.Services.AddSingleton<IPendingJobStore, InMemoryPendingJobStore>(); builder.Services.AddHttpClient("api2", c => { c.BaseAddress = new Uri("http://localhost:5000/api2"); }); builder.Services.AddHostedService<Api2RetryService>(); var app = builder.Build(); app.MapControllers(); app.Run();

Testing the Whole Flow
#1 API 2 starts in failure mode
All attempts will fail and exponential backoff kicks in.
#2 Send a request to API 1
POST /api1/process
{
  "name": "hello"
}
Job is stored.
#3 Watch logs
You’ll see:
Retrying job in 2 seconds...
Retrying job in 4 seconds...
Retrying job in 8 seconds...
...
#4 Bring API 2 back online:
POST /api2/toggle
Next retry will succeed:
Job {id} processed successfully.

Conclusion

This pattern is extremely powerful for:

  • Payment gateways
  • ERP integrations
  • Long-running partner APIs
  • Unstable third-party services
  • Internal microservices that spike or go offline

References

Read the whole story
alvinashcraft
49 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Rob Drummond on languaging and our fluid speaking identities

1 Share

In this bonus conversation with Rob Drummond from back in June, he and I get into the fascinating concept of "languaging" — the idea that speaking is an active process we use to constantly shape and project our identities. Rob explains how our "speaking identities" are incredibly fluid, changing based on context, audience, and even the language we're using. 

Rob Drummond - https://bsky.app/profile/robdrummond.bsky.social

Rob's book, "You're All Talk"

🔗 Share your familect recording via Speakpipe, by calling 833-214-4475 (or via WhatsApp chat.)

🔗 Watch my LinkedIn Learning writing courses.

🔗 Subscribe to the newsletter.

🔗 Take our advertising survey

🔗 Get the edited transcript.

🔗 Get Grammar Girl books

🔗 Join GrammarpaloozaGet ad-free and bonus episodes at Apple Podcasts or SubtextLearn more about the difference

| HOST: Mignon Fogarty

| Grammar Girl is part of the Quick and Dirty Tips podcast network.

  • Audio Engineer: Dan Feierabend
  • Director of Podcast: Holly Hutchings
  • Advertising Operations Specialist: Morgan Christianson
  • Marketing and Video: Nat Hoopes

| Theme music by Catherine Rannus.

| Grammar Girl Social Media: YouTubeTikTokFacebook.ThreadsInstagramLinkedInMastodonBluesky.


Hosted by Simplecast, an AdsWizz company. See pcm.adswizz.com for information about our collection and use of personal data for advertising.





Download audio: https://dts.podtrac.com/redirect.mp3/media.blubrry.com/grammargirl/stitcher.simplecastaudio.com/e7b2fc84-d82d-4b4d-980c-6414facd80c3/episodes/9e2b2941-abd3-4655-8f18-cfc5bcc6675e/audio/128/default.mp3?aid=rss_feed&awCollectionId=e7b2fc84-d82d-4b4d-980c-6414facd80c3&awEpisodeId=9e2b2941-abd3-4655-8f18-cfc5bcc6675e&feed=XcH2p3Ah
Read the whole story
alvinashcraft
49 minutes ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories