Self-Hosted VPN: Remote Terminal Access via Android SSH
Pi-hole + WireGuard + Unbound — with Android-to-Ubuntu SSH over VPN
Last updated: 2026 | Tested on Ubuntu 24.04 LTS, Pi-hole v6, WireGuard (kernel module), Unbound 1.19.3
Introduction
This guide sets up a self-hosted VPN server combining network-level ad blocking, privacy-focused DNS resolution, and secure remote access. The primary goal is SSH access from an Android device to an Ubuntu 24.04 PC over an encrypted WireGuard tunnel — letting you control your Ubuntu terminal from your phone, from anywhere.
What You'll Build
A cloud VPS running:
- Pi-hole — network-wide ad and tracker blocking
- Unbound — recursive DNS resolver for enhanced privacy
- WireGuard — modern, fast VPN protocol
With two clients connected:
- Ubuntu 24.04 PC — accessible via SSH from within the VPN
- Android device — SSH client connecting to the Ubuntu PC
Network Topology
Android (10.6.0.3)
|
| WireGuard VPN
v
Cloud VPS / WireGuard Server (10.6.0.1)
|
| WireGuard VPN
v
Ubuntu PC (10.6.0.2)
Android SSHes to 10.6.0.2 (Ubuntu's WireGuard IP). All traffic is encrypted. Pi-hole filters DNS for all connected devices.
Prerequisites
- Basic Linux command line knowledge
- A cloud hosting account (recommendations below)
- Ubuntu 24.04 PC with SSH server installed (
openssh-server) - Android device with WireGuard app + Termux (or another SSH client)
- SSH client on your local machine for VPS setup
- ~€5–10/month for VPS hosting
VPS minimum requirements: 1GB RAM, 1 vCPU, 10GB storage (2GB RAM recommended)
Choosing a Cloud Provider
Any VPS provider running Ubuntu 24.04 LTS works. Current options in the €3–10/month range include Hetzner, DigitalOcean, Linode, and Vultr. Choose a data centre geographically close to you for lower latency.
Note on streaming services: Most cloud IP ranges are blocked by Netflix, HBO Max, etc. This setup is focused on security, privacy, and remote access — not geo-unblocking.
Initial Server Setup
Create Your Server
- Create a new Ubuntu 24.04 LTS VPS instance
- Choose a nearby data centre
- Add your SSH public key during creation
- Note the public IP address
First Login and Updates
ssh root@your-server-ip
apt update && apt upgrade -y
apt autoremove -y
reboot
Reconnect after ~30 seconds.
Configure Firewall
apt install -y ufw
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 51820/udp
ufw enable
ufw status verbose
Install Fail2Ban
apt install -y fail2ban
systemctl enable fail2ban
systemctl start fail2ban
Enable Automatic Security Updates
apt install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades
Select Yes when prompted.
Create a Non-Root User (Recommended)
adduser username
usermod -aG sudo username
rsync --archive --chown=username:username ~/.ssh /home/username
Test SSH login as the new user before proceeding.
Installing Pi-hole
Installation
apt install -y curl
curl -sSL https://install.pi-hole.net | bash
Installation Wizard
- Upstream DNS Provider — choose any (Cloudflare or Google); you'll replace this with Unbound shortly
- Blocklists — accept defaults
- Admin Interface — Yes
- Web Server — Pi-hole v6 uses a built-in web server; lighttpd is no longer offered
- Query Logging — enable if you want to monitor DNS requests
- Privacy Mode — your preference
Note the admin password displayed at the end.
Set Admin Password
The -a -p flag was removed in Pi-hole v6. Use:
pihole setpassword
Access the Admin Interface
http://your-server-ip/admin
Enabling HTTPS with Let's Encrypt
Pi-hole v6's built-in web server supports TLS natively. This section configures HTTPS for the admin interface at https://cafe-de-bloem.whiskeytangobravo.com/admin.
Open Ports 80 and 443 Temporarily
Certbot needs port 80 to complete the domain challenge, and 443 for HTTPS traffic:
ufw allow 80/tcp
ufw allow 443/tcp
ufw reload
Install Certbot
apt install -y certbot
Obtain a Certificate
certbot certonly --standalone -d cafe-de-bloem.whiskeytangobravo.com
Certbot verifies domain ownership and saves certificates to /etc/letsencrypt/live/cafe-de-bloem.whiskeytangobravo.com/.
Configure Pi-hole to Use the Certificate
nano /etc/pihole/pihole.toml
Find the [webserver] section and set:
[webserver]
address = ":443"
[webserver.tls]
cert = "/etc/letsencrypt/live/cafe-de-bloem.whiskeytangobravo.com/fullchain.pem"
key = "/etc/letsencrypt/live/cafe-de-bloem.whiskeytangobravo.com/privkey.pem"
Restart Pi-hole FTL:
systemctl restart pihole-FTL
The admin interface is now at:
https://cafe-de-bloem.whiskeytangobravo.com/admin
Allow Pi-hole to Read the Certificate
Certbot sets restrictive permissions on the private key. Grant the pihole user access:
chmod 750 /etc/letsencrypt/live /etc/letsencrypt/archive
chgrp pihole /etc/letsencrypt/live /etc/letsencrypt/archive
chmod 640 /etc/letsencrypt/archive/cafe-de-bloem.whiskeytangobravo.com/privkey*.pem
chgrp pihole /etc/letsencrypt/archive/cafe-de-bloem.whiskeytangobravo.com/privkey*.pem
Auto-Renew the Certificate
Certbot installs a systemd timer for automatic renewal. Verify it is active:
systemctl status certbot.timer
After each renewal, Pi-hole FTL must restart to load the new certificate. Create a deploy hook:
nano /etc/letsencrypt/renewal-hooks/deploy/restart-pihole.sh
Paste:
#!/bin/bash
systemctl restart pihole-FTL
Make it executable:
chmod +x /etc/letsencrypt/renewal-hooks/deploy/restart-pihole.sh
Close Port 80 After Setup
Optionally redirect HTTP to HTTPS by adding to pihole.toml under [webserver]:
redirect_to_https = true
Or simply close port 80 if no redirect is needed:
ufw delete allow 80/tcp
ufw reload
Installing Unbound
Installation
apt install -y unbound
Configuration
nano /etc/unbound/unbound.conf.d/pi-hole.conf
Paste:
server:
verbosity: 0
interface: 127.0.0.1
port: 5335
do-ip4: yes
do-udp: yes
do-tcp: yes
do-ip6: no
num-threads: 2
msg-cache-slabs: 2
rrset-cache-slabs: 2
infra-cache-slabs: 2
key-cache-slabs: 2
rrset-cache-size: 256m
msg-cache-size: 128m
so-rcvbuf: 1m
so-sndbuf: 1m
prefetch: yes
prefetch-key: yes
aggressive-nsec: yes
qname-minimisation: yes
hide-identity: yes
hide-version: yes
harden-glue: yes
harden-dnssec-stripped: yes
harden-below-nxdomain: yes
harden-referral-path: yes
use-caps-for-id: yes
access-control: 127.0.0.1/32 allow
access-control: 0.0.0.0/0 refuse
root-hints: "/var/lib/unbound/root.hints"
# NOTE: Do not add auto-trust-anchor-file here — already configured
# in /etc/unbound/unbound.conf.d/root-auto-trust-anchor-file.conf
logfile: ""
Download Root Hints
wget -O /var/lib/unbound/root.hints https://www.internic.net/domain/named.root
Setup Trust Anchor
apt install -y unbound-anchor
rm -f /var/lib/unbound/root.key
unbound-anchor -a /var/lib/unbound/root.key
chown unbound:unbound /var/lib/unbound/root.key
chmod 644 /var/lib/unbound/root.key
Verify no duplicate trust anchor definitions:
grep -r "auto-trust-anchor-file" /etc/unbound/
It should only appear in root-auto-trust-anchor-file.conf. If it also appears in pi-hole.conf, remove it from there.
Start Unbound
unbound-checkconf
systemctl restart unbound
systemctl enable unbound
systemctl status unbound
Test Unbound
dig @127.0.0.1 -p 5335 google.com
You should see a successful DNS response.
Point Pi-hole at Unbound
- Open Pi-hole admin → Settings > DNS
- Uncheck all upstream DNS servers
- In Custom 1 (IPv4) enter:
127.0.0.1#5335 - Click Save
Installing WireGuard with PiVPN
Installation
curl -L https://install.pivpn.io | bash
Installation Wizard
- Static IP Warning — acknowledge
- User Selection — create a user (e.g.
vpnuser) if prompted - Unattended Upgrades — Yes
- VPN Protocol — select WireGuard
- Port —
51820(default) - DNS Provider — select Pi-hole
- Public IP — confirm your VPS public IP
- Reboot — allow reboot
After Reboot
pivpn list
Should show "No clients found."
Enable Client-to-Client Routing
By default PiVPN only routes traffic between clients and the server — not between clients. You need this so Android can SSH to your Ubuntu PC.
Enable IP Forwarding
sysctl net.ipv4.ip_forward
If this returns 0:
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
sysctl -p
Add iptables Forwarding Rules
Edit the WireGuard server config:
nano /etc/wireguard/wg0.conf
Add these lines in the [Interface] section:
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
Restart WireGuard:
systemctl restart wg-quick@wg0
Creating Client Profiles
Ubuntu PC Profile
pivpn add -n ubuntu-pc
Android Profile
pivpn add -n android
List All Profiles
pivpn list
Connecting the Ubuntu PC
Install WireGuard
On Ubuntu 24.04, openresolv is not available — it is not needed as systemd-resolved handles DNS natively:
sudo apt install -y wireguard
Get the Config File
If scp hangs (a known issue with some server configurations), use this instead:
ssh root@your-server-ip 'cat /home/vpnuser/configs/ubuntu-pc.conf' > ~/ubuntu-pc.conf
Verify the file was created before proceeding:
cat ~/ubuntu-pc.conf
Move it into place:
sudo mv ~/ubuntu-pc.conf /etc/wireguard/
Update AllowedIPs
Open the config:
sudo nano /etc/wireguard/ubuntu-pc.conf
The default AllowedIPs = 0.0.0.0/0, ::/0 routes all traffic through the VPN. This works fine for client-to-client routing and keeps all traffic filtered by Pi-hole. No change is required unless you want split tunnelling.
Connect
sudo wg-quick up ubuntu-pc
Disconnect:
sudo wg-quick down ubuntu-pc
Add to NetworkManager for GUI Control
Importing the profile into NetworkManager gives you a persistent VPN toggle in the Ubuntu desktop's top-right network menu — visible and clickable whether the VPN is active or not.
sudo nmcli connection import type wireguard file /etc/wireguard/ubuntu-pc.conf
Bring it up once to confirm it works:
nmcli connection up ubuntu-pc
Disable the wg-quick systemd service to avoid conflicts:
sudo systemctl disable wg-quick@ubuntu-pc
From this point, use the network menu in the top-right corner of the desktop to connect and disconnect. Auto-connect on boot:
nmcli connection modify ubuntu-pc connection.autoconnect yes
Ensure SSH Server is Running on Ubuntu PC
sudo apt install -y openssh-server
sudo systemctl enable ssh
sudo systemctl start ssh
Connecting Android
Install WireGuard App
Install WireGuard from the Google Play Store.
Get the Config
Generate a QR code on the VPS:
pivpn -qr android
In the WireGuard app:
- Tap +
- Select Create from QR code
- Scan the QR code
- Name the tunnel (e.g. "Home VPN")
- Toggle ON
Update AllowedIPs (Optional)
The default 0.0.0.0/0 routes everything through the VPN. This is fine and recommended for Pi-hole filtering. If you prefer split tunnelling, edit the profile in the app and change AllowedIPs to 10.6.0.0/24.
SSH from Android to Ubuntu PC
Install Termux
Install Termux from F-Droid (recommended over Play Store for up-to-date packages).
pkg update && pkg install openssh
Find Your Ubuntu PC's WireGuard IP
On the VPS:
wg show
Look for the peer corresponding to ubuntu-pc — its allowed ips value (e.g. 10.6.0.2/32) is its VPN IP.
Connect
In Termux, with the WireGuard VPN active on Android:
ssh mary@10.6.0.2
You should get a shell on your Ubuntu PC.
Save the Connection (Optional)
nano ~/.ssh/config
Add:
Host ubuntu-pc
HostName 10.6.0.2
User mary
Then connect with just:
ssh ubuntu-pc
Verification
Confirm VPN is Working (Android)
In Termux:
curl ifconfig.me
This should return your VPS's IP, not your local mobile IP.
Confirm Pi-hole is Filtering
nslookup doubleclick.net
This ad domain should resolve to 0.0.0.0.
Check Pi-hole Logs
Open the Pi-hole admin interface and go to Tools > Query Log. You should see queries from both 10.6.0.2 (Ubuntu) and 10.6.0.3 (Android).
Security Hardening
Restrict Pi-hole Admin to VPN Only
With HTTPS configured, you can restrict the admin interface to VPN-connected clients only. Edit pihole.toml:
nano /etc/pihole/pihole.toml
Set the address to listen only on the WireGuard interface, keeping HTTPS:
[webserver]
address = "10.6.0.1:443"
[webserver.tls]
cert = "/etc/letsencrypt/live/cafe-de-bloem.whiskeytangobravo.com/fullchain.pem"
key = "/etc/letsencrypt/live/cafe-de-bloem.whiskeytangobravo.com/privkey.pem"
Restart Pi-hole FTL:
systemctl restart pihole-FTL
The admin interface is now only reachable over HTTPS from within the VPN. Ensure port 443 is closed in UFW (external access blocked, VPN clients bypass UFW):
ufw delete allow 443/tcp
ufw reload
Harden SSH on VPS
nano /etc/ssh/sshd_config
Set:
PasswordAuthentication no
PermitRootLogin no
systemctl restart sshd
Only do this after confirming key-based SSH login works.
Maintenance
Update Pi-hole
pihole -up
Update Gravity (Blocklists)
pihole -g
Update Root Hints (Quarterly)
wget -O /var/lib/unbound/root.hints https://www.internic.net/domain/named.root
systemctl restart unbound
Monitor WireGuard Connections
wg show
pivpn -c
Troubleshooting
scp Hangs When Downloading Config Files
scp can hang due to server firewall or SFTP subsystem issues. Use cat over SSH instead:
ssh root@your-server-ip 'cat /home/vpnuser/configs/ubuntu-pc.conf' > ~/ubuntu-pc.conf
Verify the file exists before running mv:
cat ~/ubuntu-pc.conf
openresolv Not Available on Ubuntu 24.04
openresolv was removed from Ubuntu 24.04 repositories. It is not needed — install WireGuard without it:
sudo apt install -y wireguard
Android SSH Cannot Reach Ubuntu PC
- Confirm both devices show as peers with
wg showon the VPS - Confirm client-to-client iptables rules are in place (see above)
- Confirm SSH server is running on Ubuntu:
systemctl status ssh - Confirm Ubuntu's WireGuard tunnel is up:
sudo wg show - Try pinging Ubuntu from Android in Termux:
ping 10.6.0.2
Pi-hole Password Command Changed (v6)
pihole -a -p was removed in Pi-hole v6. Use:
pihole setpassword