A Debian-first guide to securely reaching your system from outside your network. The main build uses WireGuard for the private network path and SSH for the actual administration session. The goal is simple: remote access without careless exposure.
A remote access server gives you a secure way to reach your system when you are not physically present. In practical terms, that means remote administration, troubleshooting, and file management.
This guide intentionally separates remote access from public exposure. You are not going to just throw SSH onto the open internet and hope for the best. You will build a private access path first, then use SSH through that path.
Why this matters:
• You can manage your system from outside your home without relying on unsafe shortcuts.
• You reduce the chance of exposing SSH broadly to the public internet.
• You build a reusable foundation for later services like a NAS, private dashboards, and internal admin tools.
• You learn real Debian, Linux account, SSH, VPN, and firewall practices while building something useful.
WireGuard VPN + SSH over the VPN
You connect to a private VPN first, then SSH to the server using its VPN address.
Why: This avoids the lazy and risky habit of exposing SSH directly to the world.
This setup is intentionally limited to accessing the server itself. It does not route your entire home network through the VPN.
Why: That keeps the build simpler and avoids the extra complexity of IP forwarding, route design, and broader firewall policy. This guide is about secure access to the server, not full remote LAN access.
For the free self-hosted path:
• A Debian machine you control
• A stable internet connection
• Router access if manual port forwarding is needed
• A client device such as a laptop or phone
For the easier managed path:
• The same Debian machine
• A Tailscale account
• Client devices joined to the same secure network
Why this matters: Secure remote access is not just one package. It is identity, authentication, network path, and firewall policy all working together.
sudo apt update
sudo apt full-upgrade -y
What this does:
sudo apt update refreshes Debian’s package list. sudo apt full-upgrade -y
installs the currently available upgrades.
Why this matters:
You do not want to build remote access on top of stale software.
What to expect:
Package lists download, then upgrades install if any are available.
sudo apt install -y wireguard wireguard-tools openssh-server ufw qrencode
What this does:
Installs WireGuard, the WireGuard command-line tools, the OpenSSH server, UFW, and the optional QR code tool.
Why this matters:
WireGuard provides the private network path. OpenSSH provides the administration session. UFW gives you host firewall control.
What to expect:
Debian downloads and installs the packages. This may take a minute or two.
sudo systemctl enable --now ssh
sudo systemctl status ssh
What this does:
Starts the SSH service immediately and makes sure it starts automatically on boot.
Why this matters:
SSH is the actual remote administration service in this design.
What to expect:
The status output should show the service as active and running.
sudo adduser adminops
sudo usermod -aG sudo adminops
What this does:
Creates a normal user account and adds it to the sudo group.
Why this matters:
Remote administration should not happen through direct root login.
What to expect:
You will be prompted to set a password and basic account information.
ssh-keygen -t ed25519 -C "remote-admin-key"
What this does:
Creates a public/private SSH key pair on the machine you will connect from.
Why this matters:
SSH key authentication is stronger and cleaner than relying on passwords.
What to expect:
You will be asked where to save the key and whether to use a passphrase. A passphrase is strongly recommended.
ssh-copy-id adminops@SERVER-IP
What this does:
Installs your public key into the server account’s authorized keys.
Why this matters:
This is what allows you to log in with your SSH key.
What to expect:
After this, you should be able to authenticate with your key instead of a password.
sudo nano /etc/ssh/sshd_config.d/99-hardening.conf
Paste:
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AllowUsers adminops
sudo systemctl reload ssh
What this does:
Adds a small override file for SSH settings, then reloads the SSH service.
Why this matters:
• PermitRootLogin no disables direct root login
• PasswordAuthentication no disables password-based SSH logins
• KbdInteractiveAuthentication no closes another password-like path
• PubkeyAuthentication yes keeps key auth enabled
• AllowUsers adminops limits SSH access to the intended account
What to expect:
SSH reloads quietly if the file is valid.
ssh adminops@SERVER-IP
What this does:
Tests whether SSH key-based login works correctly.
Why this matters:
You should always confirm admin access works before tightening access further.
sudo mkdir -p /etc/wireguard
cd /etc/wireguard
umask 077
wg genkey | sudo tee server.key | wg pubkey | sudo tee server.pub
wg genkey | sudo tee client.key | wg pubkey | sudo tee client.pub
What this does:
Creates the WireGuard config directory, locks down default file permissions with umask 077,
then generates server and client key pairs.
Why this matters:
WireGuard authenticates peers using public/private key pairs.
What to expect:
Four key files should appear: server.key, server.pub, client.key, and client.pub.
sudo nano /etc/wireguard/wg0.conf
Paste this template:
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = YOUR_REAL_SERVER_PRIVATE_KEY
[Peer]
PublicKey = YOUR_REAL_CLIENT_PUBLIC_KEY
AllowedIPs = 10.8.0.2/32
Important:
Replace YOUR_REAL_SERVER_PRIVATE_KEY and YOUR_REAL_CLIENT_PUBLIC_KEY
with the actual contents of your key files. Do not paste the placeholder text literally.
sudo chmod 600 /etc/wireguard/wg0.conf
What this does:
Creates the WireGuard server configuration and then restricts the file so only root can read it.
Why this matters:
The config contains the server’s private key and should not be broadly readable.
Why the addresses matter:
• 10.8.0.1/24 is the server’s VPN address
• 10.8.0.2/32 is the client’s assigned VPN address
• This setup is intentionally limited to reaching the server, not the entire LAN
sudo systemctl enable --now wg-quick@wg0
sudo systemctl status wg-quick@wg0
ip a
What this does:
Starts the WireGuard tunnel, enables it at boot, checks service status, and then shows network interfaces.
Why this matters:
You want to confirm that the VPN interface actually exists before moving on.
What to expect:
You should see a wg0 interface with the address 10.8.0.1/24.
sudo ufw allow OpenSSH
sudo ufw allow 51820/udp
sudo ufw enable
sudo ufw status verbose
What this does:
Allows SSH temporarily for setup, allows WireGuard’s UDP port, enables the firewall, and shows the resulting rules.
Why this matters:
You need the VPN port open, and you want to keep admin access while you finish testing.
What to expect:
UFW should show rules for SSH and UDP port 51820.
If your server is behind a typical home router, you will usually need to forward external UDP port
51820 to the Debian server’s LAN IP.
Why this matters: Outside clients need a path to reach the WireGuard server. Some environments, such as CGNAT or managed networks, may require a different approach.
Use this template on the client device:
[Interface]
Address = 10.8.0.2/24
PrivateKey = YOUR_REAL_CLIENT_PRIVATE_KEY
[Peer]
PublicKey = YOUR_REAL_SERVER_PUBLIC_KEY
Endpoint = YOUR_PUBLIC_IP_OR_DOMAIN:51820
AllowedIPs = 10.8.0.1/32
PersistentKeepalive = 25
What this does:
Defines how the client reaches the WireGuard server and what traffic is allowed through the tunnel.
Why this matters:
• Address = 10.8.0.2/24 gives the client its VPN IP
• Endpoint tells the client where the server lives
• AllowedIPs = 10.8.0.1/32 keeps this build focused on reaching only the server
• PersistentKeepalive = 25 helps clients behind NAT stay reachable
qrencode -t ansiutf8 < client.conf
What this does:
Converts a client config file into a terminal QR code.
Why this matters:
It can be faster to import a WireGuard config into a mobile app by scanning it.
ping 10.8.0.1
ssh adminops@10.8.0.1
What this does:
Tests the VPN path first, then tests SSH through the VPN.
Why this matters:
This confirms the actual design works: private tunnel first, SSH second.
Warning: Only do this after you have confirmed the VPN works reliably. Keep a backup access method available, such as local console access or an already-open session.
sudo ufw delete allow OpenSSH
sudo ufw allow from 10.8.0.0/24 to any port 22 proto tcp
sudo ufw status numbered
What this does:
Removes the broad SSH rule and replaces it with a rule that only allows SSH from inside the WireGuard subnet.
Why this matters:
This is the real payoff of the design. SSH is no longer generally exposed. It is only reachable through the VPN.
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
What this does:
Installs Tailscale and then brings the node online in your tailnet.
Why this matters:
This path is easier for people who want secure remote access without manually handling router forwarding and WireGuard peer setup.
What to expect:
After tailscale up, you will authenticate and the node will join your secure Tailscale network.
You can then SSH to the Tailscale IP or MagicDNS name instead of the normal LAN IP.
Keep Debian updated
sudo apt update
sudo apt full-upgrade -y
Why: Most attacks target known vulnerabilities that already have patches.
Use SSH keys only
Once key-based access is confirmed, password-based SSH access should stay disabled.
Why: Password logins are a common brute-force target.
Do not leave SSH broadly exposed
Once the VPN is working, SSH should only be reachable through the VPN network.
Why: This reduces attack surface significantly.
Limit access to intended users
Use settings like AllowUsers to keep SSH access limited to the accounts you actually want remotely available.
Why: Good access control is part of good security.
Monitor service health
sudo systemctl status ssh
sudo systemctl status wg-quick@wg0
Why: Knowing when a service fails is part of operating it safely.
Keep a backup access path during setup
Do not remove your last working admin path until you have tested the new one.
Why: Misconfiguration can lock you out.
• Verify SSH is running
• Verify WireGuard is running
• Confirm the VPN client can reach 10.8.0.1
• Confirm SSH works over the VPN
• Reboot the system and confirm both services come back
sudo systemctl status ssh
sudo systemctl status wg-quick@wg0
sudo reboot
Why this matters:
A remote access build is not finished until it still works after a reboot.
• A Debian NAS you can reach privately
• A personal cloud that is only reachable through your secure access path
• A monitoring stack that stays off the public internet
• A later guide for secure access into internal services beyond just the server itself