Overview
Goal: Install and configure NGINX on a Raspberry Pi so it serves sites securely (HTTPS), supports virtual hosts, and can act as a reverse proxy for web apps.
Prerequisites
- Raspberry Pi 3/4/5 (Pi 4 or newer recommended). 2GB+ RAM for light use; 4GB+ recommended for production.
- MicroSD card (32GB+ recommended) or an SSD for better durability & speed.
- Raspberry Pi OS (64-bit) Lite or Ubuntu Server 22.04/24.04 64-bit installed and SSH enabled.
- A domain name you control (for TLS). Optional for local-only development.
- Basic familiarity with Linux command line and editing files with nano/vi.
Step 1
Update OS & Set static IP (optional but recommended)
Always start by updating packages and setting a predictable IP address for your Pi.
sudo apt update && sudo apt upgrade -y
# optional (Raspberry Pi OS): edit /etc/dhcpcd.conf or use netplan on Ubuntu
# Example (Ubuntu / netplan for a static IPv4):
# /etc/netplan/01-netcfg.yaml
network:
version: 2
ethernets:
eth0:
dhcp4: no
addresses: [192.168.1.50/24]
gateway4: 192.168.1.1
nameservers:
addresses: [8.8.8.8,8.8.4.4]
sudo netplan apply
Step 2
Install NGINX
Use the distribution package for stability. This installs a systemd service that starts on boot.
sudo apt install nginx -y
# start & enable
sudo systemctl enable --now nginx
# verify
sudo systemctl status nginx
nginx -v
Step 3
Open firewall ports (ufw) — allow HTTP/HTTPS
# install ufw if needed
sudo apt install ufw -y
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full' # opens 80 and 443
sudo ufw enable
sudo ufw status
Step 4
Create a server block (virtual host)
Put site files under /var/www/example.com
and create an NGINX server block.
# create site dir
sudo mkdir -p /var/www/example.com/html
sudo chown -R $USER:$USER /var/www/example.com/html
sudo chmod -R 755 /var/www/example.com
# sample index
cat > /var/www/example.com/html/index.html <<'EOF'
<!doctype html>
<html><head><meta charset="utf-8"/><title>Hello from Pi</title></head><body>
<h1>Hello from Raspberry Pi NGINX</h1>
</body></html>
EOF
# create server block file
sudo tee /etc/nginx/sites-available/example.com > /dev/null <<'NGCONF'
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com/html;
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
}
NGCONF
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
# test & reload
sudo nginx -t && sudo systemctl reload nginx
Step 5
Enable HTTPS with Let's Encrypt (Certbot)
Use the Certbot NGINX plugin to automatically obtain and install certificates.
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d example.com -d www.example.com
# automatic renewal is installed as a systemd timer or cron job; test with:
sudo certbot renew --dry-run
If you are behind NAT, forward ports 80 and 443 from your router to the Pi, or use DNS-based validation / a DNS provider plugin.
Step 6
Basic security hardening
- Turn off server tokens so NGINX won't advertise version numbers.
- Set up strong TLS ciphers and HSTS.
- Disable unused modules and keep the system updated.
# in /etc/nginx/nginx.conf (http block)
server_tokens off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:...';
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
# reload nginx
sudo nginx -t && sudo systemctl reload nginx
Important: Keep the cipher list up to date; use trusted SSL configuration guides (Mozilla SSL Configuration Generator) for production.
Step 7
Use NGINX as a reverse proxy (example for a Node app)
# server block (example)
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
# restart nginx after configuration changes
sudo nginx -t && sudo systemctl reload nginx
Step 8
Performance & caching tips
- Enable gzip compression.
- Use browser caching headers for static assets.
- Tune worker_processes and worker_connections in nginx.conf.
# example nginx.conf tuning (http block)
worker_processes auto;
worker_rlimit_nofile 100000;
events { worker_connections 1024; }
http {
gzip on;
gzip_min_length 1000;
gzip_proxied any;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
sendfile on;
tcp_nopush on;
}
Step 9
Monitoring, logs & backups
- Access logs:
/var/log/nginx/access.log
, error:/var/log/nginx/error.log
- Use logrotate (default) to prevent filesystem fill-up.
- Back up
/etc/nginx
and your/var/www
site content.
# quick troubleshooting commands
sudo tail -n 200 /var/log/nginx/error.log
sudo journalctl -u nginx -f
sudo nginx -t
sudo ss -ltnp | grep nginx
Step 10
Optional: Add fail2ban for brute-force protection
sudo apt install fail2ban -y
# create a basic jail for nginx (example)
sudo tee /etc/fail2ban/jail.d/nginx.conf > /dev/null <<'FJ'
[nginx-http-auth]
enabled = true
filter = nginx-http-auth
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 3
FJ
sudo systemctl restart fail2ban
Common troubleshooting
T-1
NGINX won't start
- Run
sudo nginx -t
— fix syntax or missing semicolons. - Check
/var/log/nginx/error.log
andjournalctl -u nginx
. - Ensure port 80/443 not already bound (run
ss -ltnp
).
T-2
Let's Encrypt validation failed
- Confirm DNS points to your Pi's public IP.
- Make sure ports 80 and 443 are forwarded if behind NAT.
- Use
--staging
flag to test to avoid hitting rate limits.
Wrap-up & best practices
- Prefer an SSD over SD cards for production due to wear and reliability.
- Keep the OS & NGINX updated; subscribe to security mailing lists if needed.
- Use automated backups (configs and site content) and monitor disk space.
- Consider a lightweight reverse proxy + caching layer if serving many clients.
Done: You now have a secure, maintainable NGINX server on your Raspberry Pi.