Setting Up a Raspberry Pi 5 Without a Monitor: From OS Imaging to SSH Using Only a Windows PC

Set up a Raspberry Pi 5 with no monitor or micro HDMI cable, using only a Windows PC. We use the Raspberry Pi Imager CLI and firstrun.sh to preconfigure Wi-Fi and SSH, with the full procedure and measured timings — about 3 minutes from SD card insertion to an SSH connection.

Setting Up a Raspberry Pi 5 Without a Monitor: From OS Imaging to SSH Using Only a Windows PC

Hello!

This is the Qualiteg Product Development Team!

This article kicks off a series on how to control a Raspberry Pi over the internet.

As shown in the diagram below, the series goes like this:After the headless setup (this installment), we'll harden the Raspberry Pi as a security measure in preparation for exposing it to the internet (part 2), and then make a web server running inside the Raspberry Pi directly accessible from the internet (part 3).

In this first installment, we'll cover everything from getting your hands on a Raspberry Pi to the initial setup procedure.

Figure 1: Overview of the Raspberry Pi 5 series (this installment covers part 1)
Figure 1: Overview of the series. This installment is part 1, getting to a working SSH connection using only a Windows PC

We'll walk through the steps we used to go from writing the OS to connecting over SSH using only a Windows 11 PC, with actual measured timings. From inserting the SD card and powering on to logging in via SSH took about 3 minutes.

What You'll Need

  • Raspberry Pi 5 board — we use the 4GB model in this article.
  • microSD card — 64GB this time. It gets automatically expanded to full capacity on first boot
  • Heatsink for the Raspberry Pi 5 — for thermal management
  • USB card reader — for writing the Raspberry Pi image to the SD card
  • A Windows 11 PC
  • USB-C power supply (the Raspberry Pi 5 recommends 5V/5A power, such as the official Raspberry Pi 27W USB-C Power Supply. A 5V/3A-class charger may boot depending on your configuration, but you may run into power throttling or unstable behavior)

Setting Up the Hardware

Assembly

Alright, let's get the Raspberry Pi assembled!

Here's the Raspberry Pi.

Raspberry Pi 5 board (4GB model)

First, let's attach the heatsink for thermal management.

Before attaching the heatsink, stick the included silicone-rubber-like material onto the heat-generating parts such as the CPU chip. These are what's known as thermal pads.

Applying thermal pads to the heat-generating parts

OK, with all the thermal pads in place, let's attach the heatsink itself.

Fasten the heatsink from the back side with the insulated plastic screws like this, and you're done.

The heatsink is now attached.

There's a fan power connector on the upper part of the Raspberry Pi board, so plug the power cable coming from the heatsink into it.

By the way, don't insert the SD card yet. There's nothing on it, so inserting it wouldn't do anything.

Everything up to this point should only take a few minutes.

Next, let's move on to the software.

Setting Up the Raspberry Pi Without a Monitor

Let's get right into the software setup.
This time we'll set everything up using only the CLI — in other words, a headless setup.

The environment we verified this on:

- Imaging software: Raspberry Pi Imager 2.0.8
- OS written: Raspberry Pi OS 64-bit (based on Debian 13 trixie, kernel 6.18)
- Work PC: Windows 11 Pro.

Step 1: Identify the Physical Disk Number to Write To

We'll start by preparing the SD card to insert into the Raspberry Pi.

For that, we use the dedicated imaging tool, Raspberry Pi Imager 2.0.8.

Writing to an SD card happens at the physical-disk level, so first confirm the disk number.

Raspberry Pi Imager 2.0.8 has a safety mechanism that refuses to write to devices that shouldn't be targets, such as internal drives. Even so, if you mix up another USB storage device you'll wipe its data, so always verify the disk number, capacity, and BusType.

Connect the SD card to your PC with the card reader and run the following in PowerShell.

Get-Disk
Number FriendlyName           BusType SizeGB
------ ------------           ------- ------
     0 CT4000P3PSSD8          NVMe      3726
     1 Generic- SD/MMC/MS PRO USB       59.5

In our environment, the internal SSD showed up as disk 0 and the SD card via the USB card reader as disk 1. From the capacity (59.5GB) and the BusType being USB, we can tell it's the SD card.

In the steps that follow we specify the write target as \\.\PhysicalDrive1. Be sure to substitute the disk number for your own environment.

Step 2: Get Raspberry Pi Imager and the OS Image

Raspberry Pi Imager can be installed with winget.

winget install --id RaspberryPiFoundation.RaspberryPiImager --silent --accept-package-agreements --accept-source-agreements

It installs to C:\Program Files\Raspberry Pi Ltd\Imager\. The same executable used by the GUI has a CLI mode, and a scripting wrapper, rpi-imager-cli.cmd, is bundled as well.

Get the OS image from the official download site.
The URL raspios_arm64_latest always redirects to the latest 64-bit version (with desktop), which is handy to remember.

curl.exe -L --fail -o C:\qualiteg_examples\raspi\raspios.img.xz https://downloads.raspberrypi.com/raspios_arm64_latest

The size was about 1.3GB. It can be written while still xz-compressed, so there's no need to extract it yourself.

Step 3: Create the First-Boot Script firstrun.sh

This is the heart of the headless setup.

We call the helpers imager_custom and userconf that ship with Raspberry Pi OS to configure the hostname, SSH, user, Wi-Fi, keyboard layout, and timezone all at once. Name the file firstrun.sh; it can live anywhere (you'll pass its path when writing the image in the next step).

#!/bin/bash
# set +e keeps the script running to the final cleanup even if a command fails along the way.
# If a setting doesn't take effect, check the success/failure of each command individually
set +e

/usr/lib/raspberrypi-sys-mods/imager_custom set_hostname raspberrypi
/usr/lib/raspberrypi-sys-mods/imager_custom enable_ssh
/usr/lib/userconf-pi/userconf 'pi' '$6$… (hash generated with openssl passwd -6)'
/usr/lib/raspberrypi-sys-mods/imager_custom set_wlan 'your-ssid' 'your-wifi-password' 'JP'
/usr/lib/raspberrypi-sys-mods/imager_custom set_keymap 'jp'
/usr/lib/raspberrypi-sys-mods/imager_custom set_timezone 'Asia/Tokyo'

rm -f /boot/firstrun.sh /boot/firmware/firstrun.sh
sed -i 's| systemd.run.*||g' /boot/cmdline.txt /boot/firmware/cmdline.txt 2>/dev/null
exit 0

Replace the SSID and Wi-Fi password ('your-ssid' and 'your-wifi-password' in the script above) with your own.

The user's password is passed as a SHA-512 hash, not in plain text. You can generate the hash in Git Bash or similar like this.

openssl passwd -6 'your-desired-password'

The last two lines are cleanup: the script deletes itself and restores normal boot. They remove the entry that Imager plants in the boot configuration (cmdline.txt).

There is one important caveat.

The line endings of this file must be LF.

Files created in a Windows editor tend to end up as CRLF, and with CRLF the script fails on first boot. SSH never gets enabled, Wi-Fi never connects, and with no monitor you can't even see what went wrong — a rather painful situation.

One security note as well: firstrun.sh contains your Wi-Fi password in plain text. Avoid committing it to Git or placing it in shared folders, and delete any working copies left on the Windows side once they're no longer needed.

Step 4: Write to the SD Card from the CLI

Writing to a physical disk requires administrator privileges, so run it elevated from PowerShell. Approve the UAC dialog when it appears.

$imgr = 'C:\Program Files\Raspberry Pi Ltd\Imager\rpi-imager.exe'
$opts = @('--cli', '--debug', '--disable-eject',
          '--first-run-script', 'C:\qualiteg_examples\raspi\firstrun.sh',
          '--log-file',         'C:\qualiteg_examples\raspi\flash-log.txt',
          'C:\qualiteg_examples\raspi\raspios.img.xz',
          '\\.\PhysicalDrive1')
Start-Process -FilePath $imgr -ArgumentList $opts -Verb RunAs -Wait

Here's what the options we're using mean.

OptionRole
--cliRun in command-line mode without launching the GUI
--first-run-scriptEmbed the first-boot script (firstrun.sh) into the SD card
--log-fileWrite progress and results to a log file
--debugMake the log verbose. Also records how write speed is auto-adjusted
--disable-ejectDon't auto-eject the SD card after writing completes. Add this if you want to inspect the write results

Read-back verification after writing is enabled by default. In our measurements, extracting the 1.3GB xz image took 53 seconds, and writing plus verification finished in about 5 minutes. With a slow SD card the log will show write-latency warnings, but Imager automatically adjusts the buffer size and keeps going, so just wait it out.

A note on progress display: with our setup, launching elevated via Start-Process -Verb RunAs, no progress appeared in the original PowerShell window. That's why we pass --log-file and check progress and results in the log file. If the log ends with succeeded, the write was successful.

Step 5: Power On, Wait 3 Minutes, and Connect via SSH

Insert the freshly written SD card into the Raspberry Pi 5.

Insert the micro SD card we just wrote the image to into the SD slot

Then simply connect the USB-C power supply and it boots.

Plug the power adapter into the USB-C power port; once power is supplied, the Raspberry Pi turns on.

The Raspberry Pi 5 does have a power button, but no button press is needed for a normal first boot. On first boot it runs filesystem auto-expansion, firstrun.sh, and a reboot in sequence, so wait 2–3 minutes. If the green LED on the board is blinking, the boot process is underway.

Once you've waited, connect via SSH from a Windows terminal.

ssh pi@raspberrypi.local

The name raspberrypi.local is resolved by a mechanism called mDNS. Windows 11 supports it out of the box, so no extra software is needed. In our measurements, the SSH port opened about 3 minutes after inserting the SD card and powering on, and we could log in with the user we configured.

We also checked the state after logging in. Here are excerpts from the output of uname -a, vcgencmd measure_temp, and df -h.

Linux raspberrypi 6.18.34+rpt-rpi-2712 #1 SMP PREEMPT Debian 1:6.18.34-1+rpt1 (2026-06-09) aarch64 GNU/Linux
temp=34.0'C
/dev/mmcblk0p2   58G  6.6G   49G  12% /

The entire 64GB card had been expanded into the root filesystem, the CPU temperature was 34.0°C, and an IPv4 address had been obtained over Wi-Fi.

From here on, you can use it like any ordinary Linux machine.

Pitfalls and Workarounds

Here's a table of the traps we actually hit during this work.

SymptomCauseWorkaround
When run elevated, progress may not appear in the original PowerShellWith our elevated launch via Start-Process -Verb RunAs, no progress was shown in the original PowerShell window (observed)Have it write to a log file with --log-file and check progress and results there
Writing fails with access deniedWriting to a physical disk requires administrator privilegesRun elevated with Start-Process -Verb RunAs
After first boot, no Wi-Fi connection and no SSHThe line endings of firstrun.sh are CRLF, so the script fails to executeRe-save with LF line endings and redo the write
raspberrypi.local can't be found, or an IPv6 address is returnedmDNS name resolution may prefer IPv6Usually you can connect as is. If not, check the IPv4 address in your router's admin panel and specify it directly
Automated connections from a script with plink hang on first useThe host-key confirmation prompt doesn't accept responses piped through standard inputUse -batch and -hostkey "SHA256:..." to specify the fingerprint explicitly and suppress the prompt entirely

That last plink trap is a bonus note for anyone who wants to script the whole flow up to the SSH connection. You won't hit it when typing the ssh command by hand.

Summary

A monitor-less Raspberry Pi 5 setup can be completed safely if you keep these five points in mind.

  • Confirm the physical disk number with Get-Disk before writing (most important)
  • Get the OS image from raspios_arm64_latest and write it while still xz-compressed
  • Put your Wi-Fi, SSH, and user settings into firstrun.sh. Line endings must be LF
  • Run the write with administrator privileges and check the results in the --log-file log
  • Wait 3 minutes after first boot, then ssh pi@raspberrypi.local

Once you can log in, we recommend changing the password and updating packages as your first moves.

passwd
sudo apt update && sudo apt full-upgrade -y

If you find yourself wanting a desktop, enabling VNC with sudo raspi-config lets you operate it from Windows like a remote desktop.

We plan to cover that in a separate article.

See you next time!

Read more