Step 1.1: Create the HTTP Template (.tpl)
Location: /usr/local/hestia/data/templates/web/nginx/nextjs-universal.tpl
Command to create:
#=========================================================================#
# Universal Next.js Web Domain Template #
# DO NOT MODIFY THIS FILE! CHANGES WILL BE LOST WHEN REBUILDING DOMAINS #
# https://hestiacp.com/docs/server-administration/web-templates.html #
#=========================================================================#
server {
listen %ip%:%proxy_port%;
server_name %domain_idn% %alias_idn%;
error_log /var/log/apache2/domains/%domain%.error.log error;
# Increase client max body size for file uploads
client_max_body_size 100M;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
# Next.js proxy configuration
# PORT: Change this port for each domain/user
location / {
proxy_pass http://127.0.0.1:3000;
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;
# Next.js specific headers
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# Timeout settings for long-running requests
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# WebSocket support for Next.js
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Next.js static files (if served from public_html)
location /static/ {
alias /home/%user%/web/%domain%/public_html/static/;
expires max;
add_header Cache-Control "public, immutable";
}
# Next.js media files
location /media/ {
alias /home/%user%/web/%domain%/public_html/media/;
expires max;
add_header Cache-Control "public, immutable";
}
# Next.js _next/static files (if served from public_html)
location /_next/static/ {
alias /home/%user%/web/%domain%/public_html/_next/static/;
expires max;
add_header Cache-Control "public, immutable";
}
# Health check endpoint
location /health {
proxy_pass http://127.0.0.1:3000;
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;
}
include %home%/%user%/conf/web/%domain%/nginx.conf_*;
}
Step 1.2: Create the HTTPS/SSL Template (.stpl)
Location: /usr/local/hestia/data/templates/web/nginx/nextjs-universal.stpl
Command to create:
#=========================================================================#
# Universal Next.js SSL Web Domain Template #
# DO NOT MODIFY THIS FILE! CHANGES WILL BE LOST WHEN REBUILDING DOMAINS #
# https://hestiacp.com/docs/server-administration/web-templates.html #
#=========================================================================#
server {
listen %ip%:%proxy_ssl_port% ssl;
server_name %domain_idn% %alias_idn%;
ssl_certificate /home/%user%/conf/web/%domain%/ssl/%domain%.pem;
ssl_certificate_key /home/%user%/conf/web/%domain%/ssl/%domain%.key;
error_log /var/log/apache2/domains/%domain%.error.log error;
# Increase client max body size for file uploads
client_max_body_size 100M;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
# Next.js proxy configuration
# PORT: Change this port for each domain/user
location / {
proxy_pass http://127.0.0.1:3000;
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;
# Next.js specific headers
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# Timeout settings for long-running requests
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# WebSocket support for Next.js
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Next.js static files (if served from public_html)
location /static/ {
alias /home/%user%/web/%domain%/public_html/static/;
expires max;
add_header Cache-Control "public, immutable";
}
# Next.js media files
location /media/ {
alias /home/%user%/web/%domain%/public_html/media/;
expires max;
add_header Cache-Control "public, immutable";
}
# Next.js _next/static files (if served from public_html)
location /_next/static/ {
alias /home/%user%/web/%domain%/public_html/_next/static/;
expires max;
add_header Cache-Control "public, immutable";
}
# Health check endpoint
location /health {
proxy_pass http://127.0.0.1:3000;
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;
}
include %home%/%user%/conf/web/%domain%/nginx.ssl.conf_*;
}
Step 1.3: Verify the files were created
ls -la /usr/local/hestia/data/templates/web/nginx/nextjs-universal*
- nextjs-universal.tpl (HTTP template)
- nextjs-universal.stpl (HTTPS/SSL template)
Key Differences Between the Two Files:
Feature | .tpl (HTTP) | .stpl (HTTPS/SSL) |
---|---|---|
Port | %proxy_port% | %proxy_ssl_port% |
SSL | No SSL | ssl enabled |
Certificates | None | SSL certificates included |
Include | nginx.conf_* | nginx.ssl.conf_* |
Step 2.1: Create the port manager script
Location: /usr/local/bin/nextjs-port-manager.sh
#!/bin/bash
# Next.js Port Manager for Multiple Users/Domains
# This script helps manage ports for different Next.js applications
set -e
# Configuration
PORTS_FILE="/etc/nextjs-ports.conf"
LOG_FILE="/var/log/nextjs-ports.log"
# Create ports file if it doesn't exist
if [ ! -f "$PORTS_FILE" ]; then
echo "# Next.js Port Allocation" > "$PORTS_FILE"
echo "# Format: USER:DOMAIN:PORT:SERVICE_NAME" >> "$PORTS_FILE"
echo "# Example: abzal:mclabor.abzal.net:3000:mclabor-nextjs" >> "$PORTS_FILE"
echo "" >> "$PORTS_FILE"
fi
# Function to get next available port
get_next_port() {
local start_port=3000
local max_port=3999
# Read existing ports
local used_ports=()
if [ -f "$PORTS_FILE" ]; then
while IFS=: read -r user domain port service; do
if [[ "$port" =~ ^[0-9]+$ ]]; then
used_ports+=($port)
fi
done < <(grep -v '^#' "$PORTS_FILE" | grep -v '^$')
fi
# Find next available port
for ((port=start_port; port<=max_port; port++)); do
if [[ ! " ${used_ports[@]} " =~ " ${port} " ]]; then
echo $port
return 0
fi
done
echo "No available ports in range $start_port-$max_port" >&2
return 1
}
# Function to register a new Next.js app
register_app() {
local user="$1"
local domain="$2"
local service_name="$3"
if [ -z "$user" ] || [ -z "$domain" ] || [ -z "$service_name" ]; then
echo "Usage: $0 register <user> <domain> <service_name>"
echo "Example: $0 register abzal mclabor.abzal.net mclabor-nextjs"
exit 1
fi
# Check if already registered
if grep -q "^$user:$domain:" "$PORTS_FILE"; then
echo "App already registered for $user:$domain"
grep "^$user:$domain:" "$PORTS_FILE"
return 1
fi
# Get next available port
local port=$(get_next_port)
if [ $? -ne 0 ]; then
echo "Error: $port"
exit 1
fi
# Register the app
echo "$user:$domain:$port:$service_name" >> "$PORTS_FILE"
echo "Registered: $user:$domain:$port:$service_name"
# Log the registration
echo "$(date): Registered $user:$domain on port $port" >> "$LOG_FILE"
echo "Port $port allocated for $domain"
echo "Update your nginx template to use port $port"
echo "Update your systemd service to use port $port"
}
# Function to list all registered apps
list_apps() {
echo "Registered Next.js Applications:"
echo "================================"
if [ -f "$PORTS_FILE" ]; then
while IFS=: read -r user domain port service; do
if [[ "$port" =~ ^[0-9]+$ ]]; then
echo "User: $user"
echo "Domain: $domain"
echo "Port: $port"
echo "Service: $service"
echo "Status: $(systemctl is-active $service 2>/dev/null || echo 'Not installed')"
echo "---"
fi
done < <(grep -v '^#' "$PORTS_FILE" | grep -v '^$')
else
echo "No applications registered"
fi
}
# Function to remove an app
remove_app() {
local user="$1"
local domain="$2"
if [ -z "$user" ] || [ -z "$domain" ]; then
echo "Usage: $0 remove <user> <domain>"
exit 1
fi
# Get the port and service
local line=$(grep "^$user:$domain:" "$PORTS_FILE")
if [ -z "$line" ]; then
echo "App not found for $user:$domain"
return 1
fi
IFS=: read -r user domain port service <<< "$line"
# Stop the service if running
if systemctl is-active "$service" >/dev/null 2>&1; then
echo "Stopping service $service..."
systemctl stop "$service"
systemctl disable "$service"
fi
# Remove from ports file
sed -i "/^$user:$domain:/d" "$PORTS_FILE"
echo "Removed: $user:$domain:$port:$service"
echo "$(date): Removed $user:$domain from port $port" >> "$LOG_FILE"
}
# Function to get port for a domain
get_port() {
local user="$1"
local domain="$2"
if [ -z "$user" ] || [ -z "$domain" ]; then
echo "Usage: $0 get-port <user> <domain>"
exit 1
fi
local line=$(grep "^$user:$domain:" "$PORTS_FILE")
if [ -z "$line" ]; then
echo "App not found for $user:$domain"
return 1
fi
IFS=: read -r user domain port service <<< "$line"
echo $port
}
# Function to create service file
create_service() {
local user="$1"
local domain="$2"
local port="$3"
local service_name="$4"
local service_file="/etc/systemd/system/$service_name.service"
local app_dir="/home/$user/web/$domain/public_html"
cat > "$service_file" << EOF
[Unit]
Description=Next.js application for $domain
After=network.target
[Service]
User=$user
Group=$user
WorkingDirectory=$app_dir
ExecStart=$app_dir/node_modules/.bin/next start -p $port
# Environment variables
Environment="NODE_ENV=production"
Environment="PORT=$port"
Environment="PATH=$app_dir/node_modules/.bin:/usr/local/bin:/usr/bin:/bin"
# Restart configuration
Restart=always
RestartSec=3
# Security settings
NoNewPrivileges=true
PrivateTmp=true
# Resource limits
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
echo "Service file created: $service_file"
echo "Run: sudo systemctl daemon-reload"
echo "Run: sudo systemctl enable $service_name"
echo "Run: sudo systemctl start $service_name"
}
# Main script logic
case "$1" in
"register")
register_app "$2" "$3" "$4"
;;
"list")
list_apps
;;
"remove")
remove_app "$2" "$3"
;;
"get-port")
get_port "$2" "$3"
;;
"create-service")
if [ -z "$2" ] || [ -z "$3" ] || [ -z "$4" ] || [ -z "$5" ]; then
echo "Usage: $0 create-service <user> <domain> <port> <service_name>"
exit 1
fi
create_service "$2" "$3" "$4" "$5"
;;
*)
echo "Next.js Port Manager"
echo "==================="
echo ""
echo "Usage:"
echo " $0 register <user> <domain> <service_name> - Register new app"
echo " $0 list - List all apps"
echo " $0 remove <user> <domain> - Remove app"
echo " $0 get-port <user> <domain> - Get port for domain"
echo " $0 create-service <user> <domain> <port> <service> - Create service file"
echo ""
echo "Examples:"
echo " $0 register abzal mclabor.abzal.net mclabor-nextjs"
echo " $0 list"
echo " $0 get-port abzal mclabor.abzal.net"
;;
esac
Step 2.2: Make the script executable
sudo chmod +x /usr/local/bin/nextjs-port-manager.sh
Step 2.3: Test the script
nextjs-port-manager.sh
Step 2.4: Verify the installation
# Check if the script is in PATH
which nextjs-port-manager.sh
# Check if the ports file was created
ls -la /etc/nextjs-ports.conf
# Check if the log file was created
ls -la /var/log/nextjs-ports.log
What the Port Manager Does:
✅ Manages ports - Automatically assigns ports 3000-3999
✅ Tracks applications - Keeps a database of all Next.js apps
✅ Creates services - Generates systemd service files
✅ Prevents conflicts - Ensures no two apps use the same port
✅ Easy management - Simple commands to register/list/remove apps
Available Commands:
nextjs-port-manager.sh register <user> <domain> <service_name> - Register new app
nextjs-port-manager.sh list - List all registered apps
nextjs-port-manager.sh get-port <user> <domain> - Get port for a domain
nextjs-port-manager.sh remove <user> <domain> - Remove an app
nextjs-port-manager.sh create-service <user> <domain> <port> <service> - Create service file
Step 3: Register Your First Next.js Application
1. First, let's check if the port manager script is working:
1. Check the correct script name:
ls -la /usr/local/bin/nextjs-port-manager*
2. Register your first Next.js app using the correct script name:
nextjs-port-manager.sh register abzal mclabor.abzal.net mclabor-nextjs
This command will:
Register an app for user abzal
Domain mclabor.abzal.net
Service name mclabor-nextjs
3. Check what was created:
nextjs-port-manager.sh list
4. View the generated systemd service file:
cat /etc/systemd/system/mclabor-nextjs.service
Create the symlink:
ln -s /home/abzal/web/mclabor.abzal.net/public_html/.next /home/abzal/web/mclabor.abzal.net/public_html/_next