<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: kevin scaria</title>
    <description>The latest articles on DEV Community by kevin scaria (@kevin_scaria_c0af91a296fd).</description>
    <link>https://dev.to/kevin_scaria_c0af91a296fd</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3071749%2Fc09e81d8-313c-422e-8cef-47a0f15b2de2.jpg</url>
      <title>DEV Community: kevin scaria</title>
      <link>https://dev.to/kevin_scaria_c0af91a296fd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kevin_scaria_c0af91a296fd"/>
    <language>en</language>
    <item>
      <title>How I Access My Home PC From Anywhere Without Spending a Penny</title>
      <dc:creator>kevin scaria</dc:creator>
      <pubDate>Thu, 28 May 2026 10:37:41 +0000</pubDate>
      <link>https://dev.to/kevin_scaria_c0af91a296fd/how-i-access-my-home-pc-from-anywhere-without-spending-a-penny-1an5</link>
      <guid>https://dev.to/kevin_scaria_c0af91a296fd/how-i-access-my-home-pc-from-anywhere-without-spending-a-penny-1an5</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4om1vhjj118vd55liniq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4om1vhjj118vd55liniq.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My beautiful home PC setup back in Kerala — the setup that started this project.&lt;/p&gt;

&lt;p&gt;I’m originally from Kerala, but currently residing in Mumbai.&lt;/p&gt;

&lt;p&gt;Back home, I had built a PC setup that I was genuinely proud of. It wasn’t just another computer sitting on a desk. I had spent a lot of money, time, and effort building it piece by piece.&lt;/p&gt;

&lt;p&gt;Good hardware.&lt;br&gt;
A clean setup.&lt;br&gt;
Multiple monitors.&lt;br&gt;
Everything arranged exactly the way I wanted.&lt;/p&gt;

&lt;p&gt;That setup became part of my daily life.&lt;/p&gt;

&lt;p&gt;Then I moved to Mumbai for work.&lt;/p&gt;

&lt;p&gt;And suddenly, one thought kept hitting me every single day:&lt;/p&gt;

&lt;p&gt;“How can I access my home PC from here?”&lt;/p&gt;

&lt;p&gt;I didn’t want that expensive machine sitting idle hundreds of kilometers away.&lt;/p&gt;

&lt;p&gt;The Problem&lt;/p&gt;

&lt;p&gt;At first, I started searching online for solutions.&lt;/p&gt;

&lt;p&gt;Most tutorials suggested things like:&lt;/p&gt;

&lt;p&gt;buying a Raspberry Pi paying for cloud relay services getting a static IP from ISP expensive remote desktop solutions routers with built-in DDNS support&lt;/p&gt;

&lt;p&gt;But there was a problem.&lt;/p&gt;

&lt;p&gt;I had already spent enough money building the setup itself.&lt;/p&gt;

&lt;p&gt;I didn’t want to spend more.&lt;/p&gt;

&lt;p&gt;And to make things worse, my home router didn’t even support Dynamic DNS configuration.&lt;/p&gt;

&lt;p&gt;So now I had:&lt;/p&gt;

&lt;p&gt;no static IP no DDNS support no extra hardware and no intention of paying monthly fees&lt;/p&gt;

&lt;p&gt;But I still wanted remote access.&lt;/p&gt;

&lt;p&gt;Discovering WireGuard&lt;/p&gt;

&lt;p&gt;Then one day, I discovered WireGuard VPN.&lt;/p&gt;

&lt;p&gt;Honestly, at that moment, I was over the moon.&lt;/p&gt;

&lt;p&gt;A lightweight, modern, super-fast VPN that I could host myself for free?&lt;/p&gt;

&lt;p&gt;That changed everything.&lt;/p&gt;

&lt;p&gt;Now the idea started becoming possible.&lt;/p&gt;

&lt;p&gt;If I could connect my phone or laptop to my home network using WireGuard, I could basically behave like I was sitting inside my Kerala home network — even while being in Mumbai.&lt;/p&gt;

&lt;p&gt;But there was still one huge issue.&lt;/p&gt;

&lt;p&gt;The Dynamic IP Problem&lt;/p&gt;

&lt;p&gt;Home internet connections don’t usually come with static public IPs.&lt;/p&gt;

&lt;p&gt;That means my WAN IP could change anytime because of:&lt;/p&gt;

&lt;p&gt;router reboot power failure ISP refresh reconnects&lt;/p&gt;

&lt;p&gt;So even if WireGuard worked perfectly, the connection would eventually break whenever my public IP changed.&lt;/p&gt;

&lt;p&gt;That’s when I discovered No-IP.&lt;/p&gt;

&lt;p&gt;They provided a free DDNS hostname.&lt;/p&gt;

&lt;p&gt;The moment I understood what Dynamic DNS could do, I wasn’t just over the moon anymore.&lt;/p&gt;

&lt;p&gt;I was over the solar system.&lt;/p&gt;

&lt;p&gt;Now instead of remembering an IP address, I could simply use a hostname.&lt;/p&gt;

&lt;p&gt;Something like:&lt;/p&gt;

&lt;p&gt;ximangh.ddns.net&lt;/p&gt;

&lt;p&gt;But there was still one final challenge.&lt;/p&gt;

&lt;p&gt;Whenever my public IP changes, the DDNS record also needs to update automatically.&lt;/p&gt;

&lt;p&gt;Otherwise the hostname becomes useless.&lt;/p&gt;

&lt;p&gt;So instead of manually updating it every time…&lt;/p&gt;

&lt;p&gt;I decided to automate the entire thing myself.&lt;/p&gt;

&lt;p&gt;Writing My Own Automation Script&lt;/p&gt;

&lt;p&gt;I wrote a small Python script that does exactly four things:&lt;/p&gt;

&lt;p&gt;Fetch the current public IP Compare it with the previously saved IP If the IP changed: update No-IP automatically save the new IP locally Exit silently&lt;/p&gt;

&lt;p&gt;Simple. Lightweight. No unnecessary complexity.&lt;/p&gt;

&lt;p&gt;Here’s the script:&lt;/p&gt;

&lt;p&gt;import requests&lt;br&gt;
import os&lt;br&gt;
import ipaddress&lt;br&gt;
import logging&lt;/p&gt;

&lt;p&gt;logging.basicConfig(&lt;br&gt;
    level=logging.INFO,&lt;br&gt;
    format="%(asctime)s [%(levelname)s] %(message)s"&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;NOIP_USER = os.environ.get("NOIP_USER")&lt;br&gt;
NOIP_PASS = os.environ.get("NOIP_PASS")&lt;br&gt;
HOSTNAME  = os.environ.get("DDNS_HOSTNAME", "ximangh.ddns.net")&lt;br&gt;
IP_FILE   = "public_ip.txt"&lt;/p&gt;

&lt;p&gt;def get_public_ip():&lt;br&gt;
    response = requests.get(&lt;br&gt;
        "&lt;a href="https://api.ipify.org?format=json" rel="noopener noreferrer"&gt;https://api.ipify.org?format=json&lt;/a&gt;",&lt;br&gt;
        timeout=10,&lt;br&gt;
        verify=True&lt;br&gt;
    )&lt;br&gt;
    response.raise_for_status()&lt;br&gt;
    ip = response.json()["ip"]&lt;br&gt;
    ipaddress.ip_address(ip)  # validate — raises ValueError if bad&lt;br&gt;
    logging.info(f"Fetched public IP: {ip}")&lt;br&gt;
    return ip&lt;/p&gt;

&lt;p&gt;def read_saved_ip(filename):&lt;br&gt;
    if not os.path.exists(filename):&lt;br&gt;
        logging.info("No saved IP file found.")&lt;br&gt;
        return None&lt;br&gt;
    with open(filename, "r") as file:&lt;br&gt;
        ip = file.read().strip()&lt;br&gt;
        logging.info(f"Saved IP read from file: {ip}")&lt;br&gt;
        return ip&lt;/p&gt;

&lt;p&gt;def save_ip(filename, ip):&lt;br&gt;
    with open(filename, "w") as file:&lt;br&gt;
        file.write(ip)&lt;br&gt;
    logging.info(f"New IP saved to file: {ip}")&lt;/p&gt;

&lt;p&gt;def update_noip(ip):&lt;br&gt;
    if not NOIP_USER or not NOIP_PASS:&lt;br&gt;
        logging.error("NOIP_USER or NOIP_PASS environment variable not set!")&lt;br&gt;
        return&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;response = requests.get(
    "https://dynupdate.no-ip.com/nic/update",
    params={"hostname": HOSTNAME, "myip": ip},
    auth=(NOIP_USER, NOIP_PASS),
    headers={"User-Agent": "my-ddns-updater/1.0"},
    timeout=10,
    verify=True
)

result = response.text.strip()

if result.startswith("good"):
    logging.info(f"No-IP update successful: {result}")
elif result.startswith("nochg"):
    logging.info(f"No-IP: IP unchanged (nochg): {result}")
elif result == "nohost":
    logging.error("No-IP error: Hostname not found.")
elif result == "badauth":
    logging.error("No-IP error: Invalid credentials.")
elif result == "abuse":
    logging.error("No-IP error: Account blocked for abuse.")
else:
    logging.warning(f"No-IP unknown response: {result}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;def main():&lt;br&gt;
    try:&lt;br&gt;
        current_ip = get_public_ip()&lt;br&gt;
        saved_ip   = read_saved_ip(IP_FILE)&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    if saved_ip == current_ip:
        logging.info("IP has not changed. No update needed.")
    else:
        logging.info(f"IP changed: {saved_ip} → {current_ip}")
        save_ip(IP_FILE, current_ip)
        update_noip(current_ip)

except ValueError as e:
    logging.error(f"Invalid IP address received: {e}")
except requests.RequestException as e:
    logging.error(f"Network error: {e}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;if &lt;strong&gt;name&lt;/strong&gt; == "&lt;strong&gt;main&lt;/strong&gt;":&lt;br&gt;
    main()&lt;/p&gt;

&lt;p&gt;Making It Fully Automatic&lt;/p&gt;

&lt;p&gt;The final step was to make the script run automatically.&lt;/p&gt;

&lt;p&gt;For that, I added the Python script to the Windows Startup Apps. So every time my home PC turns on, the script starts running automatically.&lt;/p&gt;

&lt;p&gt;Once the computer connects to the internet, the script checks the current WAN/public IP address using an API call. Then it compares that IP with the previously saved IP.&lt;/p&gt;

&lt;p&gt;If the IP is the same, the script does nothing.&lt;/p&gt;

&lt;p&gt;But if the IP has changed, the script updates the new WAN IP in my No-IP DDNS account. After that, my DDNS hostname automatically points to the latest WAN IP of my home network.&lt;/p&gt;

&lt;p&gt;So even after:&lt;/p&gt;

&lt;p&gt;router restarts (which usually causes my ISP to assign a new WAN/public IP)&lt;/p&gt;

&lt;p&gt;ISP IP changes&lt;/p&gt;

&lt;p&gt;…the DDNS hostname stays updated without me manually logging in anywhere.&lt;/p&gt;

&lt;p&gt;No manual checking.&lt;/p&gt;

&lt;p&gt;No updating WAN IP by hand.&lt;/p&gt;

&lt;p&gt;No logging into No-IP every time.&lt;/p&gt;

&lt;p&gt;The PC starts, the script runs, the IP gets checked, and the DNS points to the right place.&lt;/p&gt;

&lt;p&gt;The Result&lt;/p&gt;

&lt;p&gt;Now I can access my home PC from literally anywhere in the world.From Mumbai. From my mobile phone. From another network. Even while traveling.And the best part?&lt;/p&gt;

&lt;p&gt;I built the entire setup with almost zero additional cost.No Raspberry Pi. No expensive networking hardware. No static IP. No paid relay servers.&lt;/p&gt;

&lt;p&gt;Just:&lt;/p&gt;

&lt;p&gt;WireGuard No-IP Python curiosity and a little obsession with solving problems myself&lt;/p&gt;

&lt;p&gt;Coming from a security background, I did not want this project to be just functional. I wanted it to be secure, controlled, and reliable.&lt;/p&gt;

&lt;p&gt;Since this setup gives remote access to my home network, I treated it like a real security-sensitive implementation. The goal was simple: remote access should work, but it should not expose unnecessary risk.&lt;/p&gt;

&lt;p&gt;For secure communication, I used WireGuard, which uses modern cryptographic mechanisms to protect data in transit. Instead of exposing services directly to the internet, access happens through an encrypted VPN tunnel.&lt;/p&gt;

&lt;p&gt;I also restricted the VPN client configuration using a /32 address, so the client receives only one specific VPN IP address inside the tunnel. This gives better control over which client identity is allowed to connect.&lt;/p&gt;

&lt;p&gt;From the scripting side, I also followed secure coding practices instead of hardcoding everything directly into the file.&lt;/p&gt;

&lt;p&gt;Some of the improvements I added include:&lt;/p&gt;

&lt;p&gt;Storing No-IP credentials in environment variables instead of writing them directly in the script&lt;/p&gt;

&lt;p&gt;Validating the public IP before using it&lt;/p&gt;

&lt;p&gt;Enabling TLS certificate verification for API requests&lt;/p&gt;

&lt;p&gt;Adding request timeouts to avoid hanging network calls&lt;/p&gt;

&lt;p&gt;Handling network and API errors properly&lt;/p&gt;

&lt;p&gt;Logging useful events instead of using simple print statements&lt;/p&gt;

&lt;p&gt;Updating No-IP only when the WAN IP actually changes&lt;/p&gt;

&lt;p&gt;This made the script cleaner, safer, and easier to troubleshoot.&lt;/p&gt;

&lt;p&gt;What This Project Taught Me&lt;/p&gt;

&lt;p&gt;This project taught me something important:&lt;/p&gt;

&lt;p&gt;Constraints force creativity.&lt;/p&gt;

&lt;p&gt;If I had unlimited money, I probably would have purchased some ready-made solution and never learned how:&lt;/p&gt;

&lt;p&gt;VPN tunneling works Dynamic DNS works WAN IPs behave automation scripting works remote infrastructure is designed&lt;/p&gt;

&lt;p&gt;But because I wanted to avoid spending money, I ended up learning far more than I expected.&lt;/p&gt;

&lt;p&gt;And honestly?&lt;/p&gt;

&lt;p&gt;That knowledge is worth much more than the money I saved.&lt;/p&gt;

&lt;p&gt;Final Thoughts&lt;/p&gt;

&lt;p&gt;Every time I remotely connect to my Kerala PC from Mumbai, I still feel proud.&lt;/p&gt;

&lt;p&gt;Because it’s not just remote access anymore.&lt;/p&gt;

&lt;p&gt;It’s a system I engineered myself.&lt;/p&gt;

&lt;p&gt;And that feeling is honestly hard to explain.&lt;u&gt;&lt;/u&gt;&lt;/p&gt;

</description>
      <category>networking</category>
      <category>productivity</category>
      <category>sideprojects</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
