Raspberry Pi Guide
.~~. .~~.
'. \ ' ' / .'
.~ .~~~..~.
: .~.'~'.~. :
~ ( ) ( ) ~
( : '~'.~.'~' : )
~ .~ ( ) ~. ~
( : '~' : )
'~ .~~~. ~'
'~'
I've gotten increasingly into small tech over the years, and in March 2026 I finally took the plunge into the world of the Raspberry Pi.
The Raspberry Pi is a Single-Board Computer (SBC) released in 2012 by the Raspberry Pi Foundation. As of writing it is the best-selling British computer, surpassing the ZX Spectrum only three years after its launch.
The Pi was originally designed to teach computer science, but has since become a metaphorical zen garden for all manner of tech projects. As it is compact in size and consumes little energy, this makes it an ideal choice for 24/7 operations such as webservers and chatrooms. Its operating system, the Raspberry Pi OS, is a Linux-based OS. As a result, Pi-work is designed with tech enthusiasts in mind and is not a tool for casual users. To use a Pi, you have to be comfortable using a terminal and with the prospect of learning some degree of Linux.
This page serves as my personal guide for current and future Pi-rojects, as well as for any of you who want to join me.
Section Menu
Pi-Rojects
Using a UPS with your Pi Running an IRC Server on your PiPart 1: Getting a Pi
Given the low-power nature of my services, I made the more conservative choice of the Pi 4B. I purchased this from The Pi Hut, which is the most popular UK vendor for Raspberry Pis. The delivery on Pi Hut is honestly one of the fastest I've ever had from an e-vendor, even if I had to click for Royal Mail rather than have the poor thing chucked about by Evri (formerly Hermes).
What Arrived in the Post
- Raspberry Pi 4 Model B
- I purchased the 2GB option, but you can buy Pis available with up to 8GB of RAM.
- Official 32GB SD card (pre-loaded with 64-bit Raspberry Pi OS)
- This one was pre-loaded with the Pi OS, thus saving me from having to flash it to the card manually. The latter process is not covered in this guide.
- Official Raspberry Pi power supply (white UK plug)
- Always use the official Raspberry Pi power supplies, using anything else can underpower your Pi.
- A black USB cable
- This cable came as a bonus, but was ultimately left unused during my setup. It's still good to keep around as a spare.
- Official black/grey case, complete with an embossed Pi logo
- This protects the Pi's circuit board from dust, static, and physical damage. You can probably put a few little stickers on it.
What Didn't Arrive
- White micro-HDMI to HDMI cable
- Because I didn't realise my black USB cable wasn't a micro HDMI one! This is for establishing a display on your spare desktop monitor, which you'll need for navigating the Pi setup.
Part 2: Setting the scene
Starting a Pi cannot be done from your main device. You need extra hardware, as you're (temporarily) making a miniature desktop setup. If you're low on supplies, you can unplug things from your main setup. Expect the main setup to fall asleep if it's been left on; putting a mouse or keyboard back in won't automatically wake it up.
Step 1: The scene
- Spare monitor
- An Acer was used for this initial setup.
- Spare keyboard
- A spare Dell keyboard from a late Christmas gift worked nicely here.
- Spare mouse
- A Razer mouse was used, as there were no spare mice on hand.
Step 2: Checking the boxes
Once you have all of these, you're ready to assemble your Pi!
Part 3: Establishing your Pi
Be sure to keep a clean workspace and a non-metal platform for your Pi to sit on.
Step 1: Inserting the SD Card
- Slot the Pi into the bottom half of its case. Press on the flat circuit board parts of the Pi until it makes a nice snapping sound. Don't be shy, it can handle this even if it looks like it can't.
- The SD card slot is located on the underside of the Pi Board, on the other side of the USB ports. With your Pi in one hand and your Micro SD in the other, slot it gently into the Pi. The gold contacts have to face up towards you and the board its entering, while the label side faces underneath. If the card keeps entering at an angle, you're likely trying to insert the wrong side.
- Push gently until it clicks. It should not be at an angle or stick out past the edge of the bottom case.
- Slot the top case onto the Pi.
You've now loaded your Raspberry Pi. The SD card is crucial to doing anything with the Pi and it must always be handled carefully.
Step 2: Connecting Cables and Power
- Spare keyboard → any USB port on the Pi
- Razer mouse → any USB port on the Pi
- White HDMI cable:
- SMALL micro-HDMI end → Pi (use the port CLOSEST to the power port)
- Normal HDMI end → spare Acer monitor
- Make sure the spare Acer monitor is plugged in and turned on
- Power supply → plug into Pi's USB-C port LAST
When you plug in the power, the Pi will turn on and your spare monitor should start doing things!
Step 3: Navigating the Wizard
- Let the Pi boot up. It should start with a black screen, then you should see a rainbow gradient and finally the Raspberry Pi welcome screen (light-mode only so don't do this in the dark).
- You're now in the Raspberry Pi's Setup Wizard. Like other Setup Wizards, this will walk you through the installation and help you create an account for your Pi. This account is how you log into the Pi when you want to do things on it.
- Set your language, region and timezone. My Pi automatically guessed I was in England so that wasn't an issue for me personally.
- User Account: Username must be lowercase letters, digits, and hyphens only. Make sure it's got just enough to be useful while being comfortable to type; you'll have to enter this every time you want to access the Pi. As with any other account setup, write stuff down somewhere so you don't lose anything. Nothing appears on screen when typing password—this is normal.
- Network: Connect to WiFi (or skip if you're using ethernet).
- Reboot: Accept any reboot requests.
Step 4: Enabling SSH Access
Secure Shell Protocol lets you control the Pi from your main computer without needing the extra desktop setup. This is known as a "Headless" setup, and is a common practice amongst Pi users. It appears in many other services, and is a completely legitimate means of updating files, servers and more.
- Click the terminal icon (black monitor icon in top toolbar)
- Type:
sudo raspi-configand press Enter to select it. - It will ask for the password you set up back in the wizard. This logs you into your Pi account, and you'll have to do this every time you want to remotely access your Pi. If you don't see it while typing, this is normal and is common in terminals.
- A blue menu should appear. Use your keyboard's arrow keys to go to 3 Interface Options and press Enter to select it.
- Navigate to the option for SSH and select it. It will ask you "Would you like the SSH server to be enabled?", to which you select Yes.
- To finish the process, press Tab to highlight Finish and one last Enter to select.
- To check your SSH is working, type
sudo systemctl status ssh. You should see "active (running)" in green text. - Press Q to exit the menu entirely. Your SSH is now established!
Step 5: Getting Your Pi's IP Address
You're almost done with the setup, but if you stop here you won't be able to access your Pi remotely. The last thing you need is your Pi's IP Address, which you enter every time you want to access it.
- In the terminal, type:
hostname -I - Write down the number it shows. This is your Pi's IP address, and tends to differ slightly from your main computer's.
- You now have your Pi's IP Address!
IP Addresses can sometimes change for a variety of reasons, so if you cannot SSH into your Pi, you'll need to repeat this process.
Part 4: Safely turning off your Pi
Now that you've established your brand new Pi and written down its information, you're free to stop from here. However, you've probably noticed that your Pi lacks the conventional means of shut-down. Instead, it goes as follows:
Step 1: Proper Shutdown Procedure
- DO NOT, UNDER ANY CIRCUMSTANCES, pull the power cable or turn it off at the switch! This can corrupt the SD card and render it unusable!
- Every time you want to shut down your Pi, enter this command in the Terminal:
sudo shutdown now - Press Enter to execute the shutdown command. You should see a series of text in the terminal announcing the shutdown.
Step 2: Knowing when it's safe to switch off
Like other computers, the Pi does not shut down instantly. Here's how you can tell when it's safe to turn it off at the wall or remove the plug entirely.
- Your monitor screen turns off completely. The Acer I used showed a "No Signal" message.
- The LED is no longer green, leaving a red light behind. This means the Pi is powered but not fully on.
Conclusion
Congratulations, you've just set up a Raspberry Pi! Pat yourself on the back, give your pi a pat on its back and revel in the freedom of Self-Hosted Tech!
You can stop reading if this is all you needed, because from here we're going over self-hosted IRC stuff. Alternatively, you can find other guides on what to do with your brand new Pi friend!
Pi-Roject: Using a UPS with your Pi
One of the risks of running a Pi 24/7 is a sudden loss of power — whether that's a trip at the wall, a power cut, or someone accidentally kicking the cable. Without warning, this can corrupt your SD card, which as you know by now is catastrophic. The solution is a UPS HAT (Uninterruptible Power Supply Hardware Attached on Top).
This guide covers the Waveshare UPS HAT (B), which piggybacks onto your Pi via its GPIO pins and provides backup power from two 18650 lithium cells. Beyond just keeping the lights on briefly, we'll set up a background service that monitors battery level and triggers a clean, safe shutdown before the power runs out entirely.
Once everything's in place, the service runs silently in the background at all times. It checks battery level every 60 seconds, warns you when it drops below 15%, gives you 60 seconds to plug in the charger, and — if you don't — shuts the Pi down safely on its own.
What You'll Need
- Waveshare UPS HAT (B)
- The HAT itself. Note that the 18650 batteries are not included — you'll need to source those separately. Use official Waveshare batteries if possible, and do not mix old and new batteries or batteries from different manufacturers.
- 2x 18650 Lithium batteries
- These slot into the HAT and provide the backup power. Handle with care — lithium batteries can be dangerous if mishandled. Mind the polarity (+ and -).
- Barrel jack power adapter (5V recommended)
- The HAT is powered through a barrel jack rather than USB-C. This usually comes with the HAT.
- Small Phillips screwdriver
- For the mounting screws that keep the HAT attached to the Pi.
Step 1: Physical Installation
The HAT connects to your Pi via pogo pins — small spring-loaded gold contacts on the HAT's surface that press against the Pi's GPIO pads. They look a bit intimidating but they're tougher than they appear, designed for repeated connection and disconnection. If a connection doesn't work first time, you just try again.
Important: Do this before your Pi is in its case. The case needs to come off entirely for the HAT to attach — learned this one the hard way.
- Insert the batteries first, before touching the Pi. Trying to do this after connecting everything makes the case hard to open and things get fiddly fast. Check the polarity markings on the HAT and slide both cells in flush.
- Remove the Pi from its case. The case fits tightly. If it won't budge, check that the SD card is fully seated (push it in until it clicks) — a partially inserted card can jam things up.
- Check your SD card orientation before reinserting: gold contacts face up (towards the board), label side faces down. Push gently until it clicks.
- Attach the HAT. Place the HAT flat on your workspace (blue side up). Lower the Pi down onto it, lining up the GPIO header with the pogo pins. Press down firmly and steadily — think "firm handshake," not "crushing grip." You'll feel the springs compress slightly. Hold it steady while you screw down the mounting screws.
- Power on and check. Make sure the HAT's power switch is OFF, then plug in the barrel jack. Flip the switch to ON. The red LED means the batteries are charging (normal for fresh cells). Green means fully charged. The Pi's own LEDs will light up and it'll boot as usual.
If the Pi boots normally, the pogo pins are making good contact. If the HAT isn't detected later in software, see the troubleshooting section at the bottom.
Step 2: SSH In and Enable I2C
I2C is the communication protocol that lets the Pi talk to the HAT's sensor (an INA219 chip). It's disabled by default and needs to be switched on before any monitoring software will work.
SSH into your Pi as normal, then run:
sudo raspi-config
Navigate to 3 Interface Options → I5 I2C, select Yes, and reboot when prompted.
After rebooting, verify the HAT is being detected:
sudo i2cdetect -y 1
You should see a grid with 42 appearing at row 40, column 2. That's the HAT's I2C address (0x42). If you see it, you're good to go.
While you're at it, make sure the Python smbus library is installed:
sudo apt update
sudo apt install -y python3-smbus
Step 3: (Optional) Test with Waveshare's Example Script
Waveshare provides a basic monitoring script that's useful for confirming everything's working before you set up the full service. You can skip this if you're confident, but it's a good sanity check.
sudo apt-get install p7zip
wget https://files.waveshare.com/upload/4/4a/UPS_HAT_B.7z
7zr x UPS_HAT_B.7z -r -o./
cd UPS_HAT_B
python3 INA219.py
You should see readings like:
Load Voltage: 7.776 V
Current: 1.076000 A
Power: 2.416 W
Percent: 74.0%
Load Voltage is what the Pi is receiving from the HAT (healthy range is 7.5–8.4V). Current will be positive if the batteries are charging and negative if they're actively supplying power. Percent is battery charge. Press Ctrl+C to stop. Don't run this script and the monitor service simultaneously — they both read the same sensor.
Step 4: Create the Monitor Script
The monitor script is a Python program that runs as a background service, reading the battery sensor every 60 seconds and handling the warning and shutdown logic. Create it on the Pi with:
nano ~/ups_monitor.py
Paste in the following:
#!/usr/bin/env python3
"""
Waveshare UPS HAT (B) Battery Monitor & Safe Shutdown Service
"""
import time
import struct
import smbus
import subprocess
import logging
import signal
import sys
import os
from pathlib import Path
# --- Configuration ---
I2C_BUS = 1
INA219_ADDR = 0x42
CHECK_INTERVAL = 60 # Seconds between battery checks
LOW_BATTERY_PERCENT = 15 # Trigger warning at this percentage
SHUTDOWN_DELAY = 60 # Seconds to wait before shutting down
LOG_FILE = "/var/log/ups_monitor.log"
# INA219 register addresses
REG_CONFIG = 0x00
REG_SHUNT_VOLTAGE = 0x01
REG_BUS_VOLTAGE = 0x02
REG_POWER = 0x03
REG_CURRENT = 0x04
REG_CALIBRATION = 0x05
# --- Logging Setup ---
logger = logging.getLogger("ups_monitor")
logger.setLevel(logging.DEBUG)
file_handler = logging.FileHandler(LOG_FILE)
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(logging.Formatter(
"%(asctime)s [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S"
))
logger.addHandler(file_handler)
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(logging.Formatter(
"%(asctime)s [%(levelname)s] %(message)s", datefmt="%H:%M:%S"
))
logger.addHandler(console_handler)
class INA219:
def __init__(self, bus_number=I2C_BUS, address=INA219_ADDR):
self.bus = smbus.SMBus(bus_number)
self.address = address
self._configure()
def _configure(self):
self._write_register(REG_CALIBRATION, 4096)
self._write_register(REG_CONFIG, 0x399F)
def _write_register(self, reg, value):
high = (value >> 8) & 0xFF
low = value & 0xFF
self.bus.write_i2c_block_data(self.address, reg, [high, low])
def _read_register(self, reg):
data = self.bus.read_i2c_block_data(self.address, reg, 2)
value = (data[0] << 8) | data[1]
if value >= 0x8000:
value -= 0x10000
return value
def get_bus_voltage(self):
raw = self._read_register(REG_BUS_VOLTAGE)
return (raw >> 3) * 0.004
def get_current_mA(self):
self._write_register(REG_CALIBRATION, 4096)
raw = self._read_register(REG_CURRENT)
return raw * 0.1
def get_battery_percentage(self):
voltage = self.get_bus_voltage()
max_voltage = 8.4
min_voltage = 6.0
if voltage >= max_voltage:
return 100.0
if voltage <= min_voltage:
return 0.0
percent = ((voltage - min_voltage) / (max_voltage - min_voltage)) * 100.0
return round(percent, 1)
def notify_users(message):
try:
subprocess.run(["wall", message], timeout=5, capture_output=True)
except Exception as e:
logger.warning(f"Could not send wall message: {e}")
try:
notice_path = Path("/home/pearlnight/ups_notice.txt")
with open(notice_path, "w") as f:
f.write(f"{time.strftime('%Y-%m-%d %H:%M:%S')} -- {message}\n")
except Exception:
pass
def cancel_shutdown():
try:
subprocess.run(["sudo", "shutdown", "-c"], timeout=5, capture_output=True)
logger.info("Pending shutdown cancelled.")
except Exception as e:
logger.warning(f"Could not cancel shutdown: {e}")
def initiate_shutdown():
logger.critical("Executing system shutdown NOW.")
notify_users("UPS BATTERY CRITICAL -- System is shutting down NOW to protect your data.")
try:
subprocess.run(
["sudo", "shutdown", "-h", "+1",
"UPS battery critically low -- automatic safe shutdown"],
timeout=5, capture_output=True
)
except Exception as e:
logger.error(f"Shutdown command failed: {e}")
try:
subprocess.run(["sudo", "poweroff"], timeout=5)
except Exception:
pass
running = True
def handle_signal(signum, frame):
global running
logger.info(f"Received signal {signum} -- stopping monitor.")
running = False
signal.signal(signal.SIGTERM, handle_signal)
signal.signal(signal.SIGINT, handle_signal)
def main():
global running
logger.info("=" * 60)
logger.info("UPS Monitor starting up")
logger.info(f" I2C address : 0x{INA219_ADDR:02X}")
logger.info(f" Check every : {CHECK_INTERVAL}s")
logger.info(f" Low battery : {LOW_BATTERY_PERCENT}%")
logger.info(f" Shutdown delay: {SHUTDOWN_DELAY}s")
logger.info("=" * 60)
sensor = None
retry_count = 0
while running and sensor is None:
try:
sensor = INA219()
logger.info("INA219 sensor initialised successfully.")
except Exception as e:
retry_count += 1
logger.error(f"Cannot reach INA219 (attempt {retry_count}): {e}")
if retry_count >= 5:
logger.critical("Failed to reach INA219 after 5 attempts. Exiting.")
sys.exit(1)
time.sleep(10)
shutdown_pending = False
shutdown_timer_start = None
while running:
try:
voltage = sensor.get_bus_voltage()
current = sensor.get_current_mA()
percent = sensor.get_battery_percentage()
logger.info(
f"Battery: {percent:.1f}% | "
f"Voltage: {voltage:.2f}V | "
f"Current: {current:.1f}mA"
)
if percent >= LOW_BATTERY_PERCENT:
if shutdown_pending:
logger.info(f"Power restored! Battery back to {percent:.1f}%. Cancelling shutdown.")
cancel_shutdown()
notify_users(f"Power restored -- battery at {percent:.1f}%. Shutdown cancelled.")
shutdown_pending = False
shutdown_timer_start = None
else:
if not shutdown_pending:
logger.warning(f"Battery LOW at {percent:.1f}%! Shutdown in {SHUTDOWN_DELAY} seconds.")
notify_users(
f"WARNING: UPS battery at {percent:.1f}%!\n"
f"System will shut down in {SHUTDOWN_DELAY} seconds to protect your data.\n"
"Plug in the charger to cancel."
)
shutdown_pending = True
shutdown_timer_start = time.time()
else:
elapsed = time.time() - shutdown_timer_start
remaining = max(0, SHUTDOWN_DELAY - elapsed)
logger.warning(f"Battery still low at {percent:.1f}%. Shutdown in {remaining:.0f}s.")
if elapsed >= SHUTDOWN_DELAY:
initiate_shutdown()
time.sleep(120)
sys.exit(0)
except OSError as e:
logger.error(f"I2C read error: {e}. Will retry next cycle.")
except Exception as e:
logger.error(f"Unexpected error: {e}", exc_info=True)
for _ in range(CHECK_INTERVAL):
if not running:
break
time.sleep(1)
logger.info("UPS Monitor stopped cleanly.")
if __name__ == "__main__":
main()
Save with Ctrl+O, Enter, Ctrl+X.
The settings you're most likely to want to tweak are near the top of the script:
CHECK_INTERVAL = 60- How often (in seconds) to read the battery sensor.
LOW_BATTERY_PERCENT = 15- The percentage at which the warning fires and the countdown begins.
SHUTDOWN_DELAY = 60- How many seconds you have to plug the charger back in before the Pi shuts itself down.
Step 5: Create the systemd Service File
The service file tells systemd to run the monitor script automatically on boot and restart it if it ever crashes.
nano ~/ups-monitor.service
Paste in:
[Unit]
Description=Waveshare UPS HAT (B) Battery Monitor
Documentation=https://www.waveshare.com/wiki/UPS_HAT_(B)
After=multi-user.target
Wants=multi-user.target
[Service]
Type=simple
ExecStart=/usr/bin/python3 /opt/ups_monitor/ups_monitor.py
Restart=on-failure
RestartSec=10
User=root
StandardOutput=journal
StandardError=journal
StartLimitIntervalSec=300
StartLimitBurst=5
[Install]
WantedBy=multi-user.target
Save and exit.
Step 6: Install and Start the Service
sudo mkdir -p /opt/ups_monitor
sudo cp ~/ups_monitor.py /opt/ups_monitor/ups_monitor.py
sudo chmod 755 /opt/ups_monitor/ups_monitor.py
sudo cp ~/ups-monitor.service /etc/systemd/system/ups-monitor.service
sudo touch /var/log/ups_monitor.log
sudo chmod 644 /var/log/ups_monitor.log
Before enabling the service, it's worth running the script manually once to confirm it's reading the sensor correctly:
sudo python3 /opt/ups_monitor/ups_monitor.py
You should see something like:
16:11:16 [INFO] UPS Monitor starting up
16:11:16 [INFO] INA219 sensor initialised successfully.
16:11:16 [INFO] Battery: 83.5% | Voltage: 8.00V | Current: 920.9mA
Press Ctrl+C once you're happy. Now enable and start it as a service:
sudo systemctl daemon-reload
sudo systemctl enable ups-monitor.service
sudo systemctl start ups-monitor.service
sudo systemctl status ups-monitor.service
You're looking for active (running) in the status output. Your Pi's SD card is now protected.
How Warnings Reach You
Since the Pi runs headless, warnings come through two ways. If you're logged in via SSH, a broadcast wall message will appear directly in your terminal. If you're not logged in, the script writes to /home/pearlnight/ups_notice.txt, which you can check at any time with:
cat ~/ups_notice.txt
If nobody is logged in at all, the Pi will still shut itself down safely before the power runs out — which is the whole point.
Troubleshooting
- HAT not detected / "Cannot reach INA219" errors
- Check I2C is enabled (
sudo raspi-config nonint get_i2cshould print 0) and runsudo i2cdetect -y 1to see if 42 appears. Make sure the HAT is firmly seated. If the pogo pins still aren't making contact, unscrew the HAT, adjust the alignment slightly, and press down more firmly before screwing it back. Waveshare's docs note you can press each pin gently, or very lightly scratch the GPIO pads with a knife to clear any oxide layer. - Service won't start
- Check the logs with
journalctl -u ups-monitor.service -n 50. Also verify smbus is installed:python3 -c "import smbus; print('OK')" - SSH "Permission denied" or "No route to host"
- Double-check your username and password. If you're getting "No route to host," your VPN is probably on — turn it off before SSHing into your local Pi.
Quick Reference
- Check if running
sudo systemctl status ups-monitor.service- Start / Stop / Restart
sudo systemctl start ups-monitor.service
sudo systemctl stop ups-monitor.service
sudo systemctl restart ups-monitor.service- Watch the log live
tail -f /var/log/ups_monitor.log- Check notice file
cat ~/ups_notice.txt- Check I2C / HAT detection
sudo i2cdetect -y 1
Pi-Roject: Running an IRC Server on your Pi
This guide covers setting up Dreadfortress, a self-hosted IRC server running on the Pi using Ergo IRC. It goes from a bare Pi to a fully working setup: a domain that tracks your home IP, a real TLS certificate, Ergo running as a systemd service, and connections working via both desktop client (HexChat) and browser (KiwiIRC). Written based on the actual setup session, mistakes and all.
This is not a beginner guide — it assumes you've already got your Pi set up and can SSH into it comfortably.
Overview
- Ergo IRC
- The IRC server software running on the Pi. Version used: ergo-2.14.0-linux-arm64.
- No-IP DUC
- A Dynamic DNS client that keeps your domain (dreadfortress.ddns.net) pointed at your home IP even when it changes.
- Win-ACME
- Runs on Windows and handles automatic TLS certificate renewal via Let's Encrypt.
- HexChat
- Desktop IRC client for connecting via port 6697 (TLS).
- KiwiIRC
- Browser-based IRC client for connecting via port 8097 (websocket over TLS).
What ended up running where: No-IP DUC lives on the Pi as a systemd service. Win-ACME lives on Windows and handles cert renewal, then copies the renewed certs to the Pi automatically via a batch script. Ergo itself lives on the Pi and restarts automatically after certs are renewed.
Step 1: SSH into the Pi
From your Windows terminal (VPN off):
ssh pearlnight@192.168.1.132
Step 2: No-IP DUC on the Pi
This keeps your domain pointing at your home IP automatically. If you've previously been running the No-IP client on Windows, this replaces it.
cd /tmp
wget https://www.noip.com/client/linux/noip-duc-linux.tar.gz
tar xzf noip-duc-linux.tar.gz
cd noip-2.1.9-1
sudo make install
Follow the prompts: enter your email and password, select your hostname group, set the update interval to 30 minutes, and say no to running a script on update.
Create the systemd service file:
sudo nano /etc/systemd/system/noip2.service
Paste in:
[Unit]
Description=No-IP Dynamic DNS Update Client
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/bin/noip2
Restart=on-failure
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable noip2
sudo systemctl start noip2
sudo systemctl status noip2
You should see active (running) and enabled.
Step 3: TLS Certificates (Win-ACME on Windows)
Let's Encrypt certificates are what make TLS connections to the server trustworthy. Win-ACME handles obtaining and automatically renewing them, but there's a catch worth knowing about.
Why cert renewal runs on Windows, not the Pi: When Win-ACME verifies your domain, Let's Encrypt contacts your external IP on port 80. On a UK residential connection, ISPs commonly block inbound port 80 — it's a standard measure to discourage running web servers on home lines. Port 443 and higher ports like 6697 are usually fine. Since Win-ACME was already set up on Windows and working there, the simplest fix is to keep it there and automate pushing the renewed certs across to the Pi.
Important: The router rule for port 80 must point to your Windows machine, not the Pi. Check this before the next renewal date.
Setting Up Passwordless SSH from Windows to Pi
This is needed so the cert copy script can run without a password prompt. Run these on Windows as Administrator:
ssh-keygen -t rsa -b 4096
(hit Enter through all prompts — no passphrase)
type C:\Users\black\.ssh\id_rsa.pub | ssh pearlnight@192.168.1.132 "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
Enter the Pi password when prompted. Test it works by running ssh pearlnight@192.168.1.132 — it should connect with no password.
The Cert Copy Script
Create this file at C:\Users\black\Documents\copy-certs-to-pi.bat:
@echo off
scp C:\Users\black\Documents\Ergo\certs\dreadfortress.ddns.net-chain.pem pearlnight@192.168.1.132:/home/pearlnight/ergo/certs/fullchain.pem
scp C:\Users\black\Documents\Ergo\certs\dreadfortress.ddns.net-key.pem pearlnight@192.168.1.132:/home/pearlnight/ergo/certs/privkey.pem
ssh pearlnight@192.168.1.132 "sudo systemctl restart ergo"
This copies the renewed certs to the Pi and restarts Ergo so it picks them up. Add this as a post-renewal installation step in Win-ACME so it runs automatically after every renewal.
Step 4: Create the Ergo systemd Service File
On the Pi:
sudo nano /etc/systemd/system/ergo.service
Paste in:
[Unit]
Description=Ergo IRC Server
After=network.target
[Service]
Type=simple
User=pearlnight
WorkingDirectory=/home/pearlnight/ergo/ergo-2.14.0-linux-arm64
ExecStart=/home/pearlnight/ergo/ergo-2.14.0-linux-arm64/ergo run --conf /home/pearlnight/ergo/ergo-2.14.0-linux-arm64/ircd.yaml
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
Note the --conf flag on the ExecStart line. Easy to miss, and Ergo will fail silently without it.
Save with Ctrl+O, Enter, Ctrl+X. The asterisk (*) in nano's title bar means you have unsaved changes — make sure it's gone before you exit.
Step 5: Enable and Start Ergo
sudo systemctl daemon-reload
sudo systemctl enable ergo
sudo systemctl start ergo
sudo systemctl status ergo
You're looking for active (running) in the status output, along with log lines like info : server : Server running. If something goes wrong:
journalctl -u ergo -f
Step 6: Disable Plaintext (TLS Only)
When Ergo first starts, it warns that port 6667 (plaintext IRC) is active. Since we want TLS-only connections, this needs to be commented out.
nano /home/pearlnight/ergo/ergo-2.14.0-linux-arm64/ircd.yaml
Find the listeners section. It will look something like this:
listeners:
":6667":
":6697":
tls:
cert: certs/fullchain.pem
key: certs/privkey.pem
":8097":
websocket: true
tls:
cert: certs/fullchain.pem
key: certs/privkey.pem
To disable port 6667, add a # at the very start of that line, before the quote mark. Nano may highlight it in a different colour — that doesn't mean it's commented out. Only an actual # character at the start of the line counts:
Wrong: ":6667":
Right: #":6667":
Save, then restart Ergo:
sudo systemctl restart ergo
Verify port 6667 is no longer listening (this should return nothing):
ss -tlnp | grep 6667
And check the cert expiry while you're at it:
openssl x509 -in /home/pearlnight/ergo/certs/fullchain.pem -noout -dates
Step 7: Port Forwarding on the Router
External connections won't work unless the right ports are forwarded to the Pi at the router level. The router admin page is usually at 192.168.1.254 for a BT Home Hub.
The rules you need:
- Port 6697 → Pi
- TLS IRC connections (HexChat and other desktop clients).
- Port 8097 → Pi
- Websocket TLS connections (KiwiIRC and other browser clients).
- Port 80 → Windows machine
- Used by Win-ACME for Let's Encrypt domain verification at cert renewal time. Must not point to the Pi.
A common mistake here is having 6697 and 8097 pointing at the wrong machine. If external connections aren't working, check these rules first.
Step 8: Connect with HexChat
- HexChat menu → Network List (or Ctrl+S)
- Click Add, name it "DreadFortress"
- Add server:
dreadfortress.ddns.net/6697(or the local IP for LAN only:192.168.1.132/6697) - Tick Use SSL for all the servers on this network
- Tick Accept invalid SSL certificates
- Click Connect
If you get "Disconnected (Remote host closed socket)", it's almost certainly because SSL wasn't ticked. The server only accepts TLS — an unencrypted connection gets immediately rejected.
Step 9: Connect with KiwiIRC
- Go to kiwiirc.com/nextclient/
- Set Server:
dreadfortress.ddns.net - Set Port:
8097 - Scroll down to Advanced
- Tick Direct websocket
- Set the websocket URL to:
wss://dreadfortress.ddns.net:8097/ - Click Network
Two things to watch out for here. First, the direct websocket field defaults to ws:// (unencrypted) — it must be wss://. Second, if it still won't connect, diagnose with:
curl -v https://dreadfortress.ddns.net:8097 2>&1 | head -30
A timeout here usually means port 8097 isn't being forwarded through the router. Fix the port forwarding rule and it'll connect immediately.
Quick Reference
- Check if Ergo is running
sudo systemctl status ergo- Start / Stop / Restart Ergo
sudo systemctl start ergo
sudo systemctl stop ergo
sudo systemctl restart ergo- View live Ergo logs
journalctl -u ergo -f- Check which ports are listening
ss -tlnp | grep ergo- Edit Ergo config
nano /home/pearlnight/ergo/ergo-2.14.0-linux-arm64/ircd.yaml- After editing the service file, always run
sudo systemctl daemon-reload- Check cert expiry on Pi
openssl x509 -in /home/pearlnight/ergo/certs/fullchain.pem -noout -dates- Check if No-IP is running
sudo systemctl status noip2- Manually copy certs from Windows
C:\Users\black\Documents\copy-certs-to-pi.bat