
Deploying my own website on a home server
Deploying my own website on a home server
This article documents the complete process of deploying a Next.js application on a Raspberry Pi, making it accessible from the internet with HTTPS. We'll use cv.roman-smal.uk as our real-world example throughout this guide.
Overview
Deploying a Next.js application on a Raspberry Pi offers a cost-effective way to host personal projects and portfolios. While cloud platforms like Vercel and Netlify provide excellent hosting solutions, self-hosting on a Raspberry Pi gives you complete control over your infrastructure and valuable hands-on experience with server administration, networking, and DevOps practices.
This setup provides:
- Full control over your hosting environment - No vendor lock-in, complete customization
- Low cost compared to cloud hosting - One-time hardware investment (~$35-75) plus minimal electricity costs
- Learning opportunity for DevOps and system administration - Real-world experience with Linux, systemd, Nginx, and SSL
- Production-ready setup with SSL and reverse proxy - Professional-grade deployment with automatic restarts and security
- Privacy and data ownership - Your data stays on your hardware, no third-party access
- Scalable foundation - Learn concepts that apply to larger deployments and cloud infrastructure
- Educational value - Understand how web applications work from server to browser
Whether you're a developer looking to host a personal portfolio, a student learning DevOps, or someone interested in self-hosting, this guide walks you through every step of deploying a production-ready Next.js application on a Raspberry Pi with proper SSL encryption and reverse proxy configuration.
Architecture
Our deployment uses:
- Next.js 14 - React framework with server-side rendering
- Systemd - Service management for automatic startup and restarts
- Nginx - Reverse proxy and SSL termination
- Let's Encrypt - Free SSL certificates via Certbot
- Raspberry Pi - ARM-based single-board computer running Arch Linux
Prerequisites
- Raspberry Pi with Arch Linux (or similar Linux distribution)
- Node.js 18+ installed
- Domain name (optional but recommended)
- Router with port forwarding capability
- Basic command-line knowledge
Step 1: Building and Transferring the Application
Build Locally
First, build your Next.js application for production:
npm run build
This creates an optimised production build in the .next directory.
Transfer to Raspberry Pi
Transfer your project to the Raspberry Pi using SCP or Git:
# Using SCP
scp -r /path/to/your/blog pi@raspberry-pi-ip:/home/pi/blog
# Or clone from Git repository
ssh pi@raspberry-pi-ip
cd /home/pi
git clone your-repo-url blog
Install Dependencies
On the Raspberry Pi:
cd /home/pi/blog
npm install --production
npm run build
Step 2: Creating a Systemd Service
Systemd ensures your application starts automatically on boot and restarts on failure.
Service Configuration
Create /etc/systemd/system/blog.service:
[Unit]
Description=Next.js Blog Application
After=network.target
[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/blog
Environment=NODE_ENV=production
Environment=PORT=3010
ExecStart=/usr/bin/npm start
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Key points:
User=pi- Runs as the pi user (adjust to your username)WorkingDirectory- Path to your applicationPORT=3010- Internal port for Next.jsRestart=on-failure- Automatically restarts on crashes
Enable and Start Service
sudo systemctl daemon-reload
sudo systemctl enable blog.service
sudo systemctl start blog.service
sudo systemctl status blog.service
Verify it's running:
curl http://localhost:3010
Step 3: Configuring Nginx as Reverse Proxy
Nginx acts as a reverse proxy, handling SSL termination and routing requests to your Next.js application.
Install Nginx
sudo pacman -S nginx
sudo systemctl enable nginx
sudo systemctl start nginx
Create Nginx Configuration
Create /etc/nginx/conf.d/blog.conf:
# HTTP server - redirect to HTTPS
server {
listen 80;
server_name cv.roman-smal.uk;
# Redirect all HTTP to HTTPS
return 301 https://$server_name$request_uri;
}
# HTTPS server
server {
listen 443 ssl http2;
server_name cv.roman-smal.uk;
# SSL certificates (will be configured by Certbot)
ssl_certificate /etc/letsencrypt/live/cv.roman-smal.uk/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/cv.roman-smal.uk/privkey.pem;
# SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Proxy to Next.js
location / {
proxy_pass http://localhost:3010;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
Update Main Nginx Config
Ensure /etc/nginx/nginx.conf includes your config:
http {
# ... other config ...
# Include configs from conf.d (must be inside http block)
include /etc/nginx/conf.d/*.conf;
# ... rest of config ...
}
Test and Reload
sudo nginx -t
sudo systemctl reload nginx
Step 4: Setting Up SSL with Let's Encrypt
Let's Encrypt provides free SSL certificates valid for 90 days with automatic renewal.
Install Certbot
sudo pacman -S certbot
Obtain Certificate
Using standalone mode (since we're not using the nginx plugin):
# Stop nginx temporarily
sudo systemctl stop nginx
# Get certificate
sudo certbot certonly --standalone -d cv.roman-smal.uk
# Start nginx
sudo systemctl start nginx
Certificates are saved to /etc/letsencrypt/live/cv.roman-smal.uk/.
Configure Auto-Renewal
Certbot creates a systemd timer for automatic renewal:
# Enable auto-renewal
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer
# Test renewal
sudo certbot renew --dry-run
Certificates automatically renew 30 days before expiration.
Step 5: Network Configuration
Router Port Forwarding
Configure your router to forward:
- Port 80 → Raspberry Pi IP, port 80 (HTTP redirect)
- Port 443 → Raspberry Pi IP, port 443 (HTTPS)
Firewall Configuration
Allow HTTP and HTTPS traffic:
# Using UFW
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Or using firewalld
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
DNS Configuration
Point your domain to your public IP:
- Add an A record:
cv.roman-smal.uk→your-public-ip - Wait for DNS propagation (usually 15-30 minutes)
- Verify:
dig cv.roman-smal.uk
Step 6: Security Considerations
Production Optimizations
Update next.config.js for production:
const nextConfig = {
reactStrictMode: true,
// Disable source maps in production
productionBrowserSourceMaps: false,
// Remove console logs
compiler: {
removeConsole: process.env.NODE_ENV === 'production' ? {
exclude: ['error', 'warn'],
} : false,
},
// Security headers
async headers() {
return [
{
source: '/:path*',
headers: [
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-XSS-Protection', value: '1; mode=block' },
],
},
]
},
}
System Updates
Keep your system updated:
sudo pacman -Syu
Troubleshooting
Service Not Starting
Check logs:
sudo journalctl -u blog.service -n 50
Common issues:
- Build missing: Run
npm run build - Wrong user: Update
User=in service file - Port in use: Change
PORT=environment variable
Nginx Not Proxying
Check error logs:
sudo tail -f /var/log/nginx/error.log
Verify:
- Next.js is running:
curl http://localhost:3010 - Nginx config syntax:
sudo nginx -t - Ports are listening:
sudo ss -tlnp | grep -E ':(80|443|3010)'
SSL Certificate Issues
Verify certificate files:
sudo ls -la /etc/letsencrypt/live/cv.roman-smal.uk/
Check file permissions and paths in nginx config.
Monitoring
View Logs
# Next.js logs
sudo journalctl -u blog.service -f
# Nginx access logs
sudo tail -f /var/log/nginx/access.log
# Nginx error logs
sudo tail -f /var/log/nginx/error.log
Check Service Status
sudo systemctl status blog.service
sudo systemctl status nginx
Real-World Example
This setup is currently running at cv.roman-smal.uk, serving a Next.js portfolio and blog. The deployment includes:
- Automatic service restarts on failure
- SSL/TLS encryption via Let's Encrypt
- HTTP to HTTPS redirects
- Production-optimised Next.js build
- Reverse proxy with proper headers
Performance Considerations
Raspberry Pi limitations:
- Memory: Monitor with
free -h - CPU: Check with
htop - Storage: Monitor with
df -h
For high-traffic sites, consider:
- Cloud hosting (Vercel, Netlify)
- More powerful hardware
- CDN for static assets
Cost Breakdown
- Raspberry Pi: One-time ~$35-75
- Domain: ~$8-12/year
- SSL Certificate: Free (Let's Encrypt)
- Electricity: ~$2-5/year
- Total: ~$10-17/year after initial hardware
Conclusion
Deploying Next.js on a Raspberry Pi is an excellent learning experience and cost-effective solution for personal projects. The setup provides production-ready features including SSL, automatic restarts, and reverse proxy configuration.
This deployment demonstrates practical DevOps skills including:
- System service management
- Reverse proxy configuration
- SSL/TLS certificate management
- Network configuration
- Production optimization
For the live example, visit cv.roman-smal.uk.