{"id":4928,"date":"2026-02-18T05:45:28","date_gmt":"2026-02-18T05:45:28","guid":{"rendered":"https:\/\/softcolontechnologies.com\/blogs\/?p=4928"},"modified":"2026-02-18T05:46:29","modified_gmt":"2026-02-18T05:46:29","slug":"step-by-step-guide-to-deploying-a-node-js-app-on-a-vps","status":"publish","type":"post","link":"https:\/\/www.softcolon.com\/blogs\/step-by-step-guide-to-deploying-a-node-js-app-on-a-vps\/","title":{"rendered":"Step-by-Step Guide to Deploying a Node.js App on a VPS"},"content":{"rendered":"<h2 class=\"text-3xl font-semibold mt-14 mb-8 \">Introduction:<\/h2>\n<p class=\" text-lg my-6\">When you deploy a Node.js application to production, you face several challenges:<\/p>\n<ol class=\"list-decimal ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>Port Access:<\/strong> Node.js apps run on high ports (3000, 3001, etc.) that require special permissions. HTTP\/HTTPS use ports 80 and 443, which only the root user can access directly.<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>HTTPS Management:<\/strong> Setting up SSL\/TLS certificates manually is complex. You need to obtain certificates, renew them before expiry, and handle multiple domains.<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>Multiple Apps:<\/strong> If you want to run multiple Node.js apps on one server, you need something to route traffic to each one.<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>Security:<\/strong> Exposing Node.js directly to the internet is risky. A reverse proxy sits in front and adds security layers.<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>Performance:<\/strong> The reverse proxy can compress responses, cache content, and handle HTTPS termination so your app doesn&#8217;t have to.<\/p>\n<\/li>\n<\/ol>\n<p class=\" text-lg my-6\"><strong>Caddy solves all of these problems.<\/strong> It&#8217;s a modern web server that:<\/p>\n<ul class=\"list-disc ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Automatically manages HTTPS certificates<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Routes traffic to your Node.js app<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Applies security headers and protections<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Handles multiple domains<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Requires minimal configuration<\/p>\n<\/li>\n<\/ul>\n<p class=\" text-lg my-6\">This guide walks you through setting up Caddy as a reverse proxy for a Node.js application, from bare server to secure production deployment.<\/p>\n<hr \/>\n<h2 class=\"text-3xl font-semibold mt-14 mb-8 \">Architecture: How It All Fits Together<\/h2>\n<p class=\" text-lg my-6\">Before we start, let&#8217;s understand the complete architecture:<\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-json whitespace-pre-wrap break-words text-gray-300\">Internet\n   \u2193\nDomain<span class=\"hljs-punctuation\">:<\/span> example.com (resolves to your VPS IP)\n   \u2193\n<span class=\"hljs-punctuation\">[<\/span>Caddy Web Server<span class=\"hljs-punctuation\">]<\/span> \u2190 Listens on ports <span class=\"hljs-number\">80<\/span> &amp; <span class=\"hljs-number\">443<\/span>\n   \u2193\n<span class=\"hljs-punctuation\">[<\/span>PM2 Process Manager<span class=\"hljs-punctuation\">]<\/span>\n   \u2193\n<span class=\"hljs-punctuation\">[<\/span>Node.js App<span class=\"hljs-punctuation\">]<\/span> \u2190 Runs on port <span class=\"hljs-number\">3000<\/span> (internal only)\n   \u2193\n<span class=\"hljs-punctuation\">[<\/span>Database\/Cache<span class=\"hljs-punctuation\">]<\/span> \u2190 Your app's data layer\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\"><strong>Data flow:<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-json whitespace-pre-wrap break-words text-gray-300\">User visits https<span class=\"hljs-punctuation\">:<\/span><span class=\"hljs-comment\">\/\/example.com<\/span>\n    \u2193\nBrowser connects to VPS port <span class=\"hljs-number\">443<\/span> (HTTPS)\n    \u2193\nCaddy receives the request\n    \u2193\nCaddy applies security headers<span class=\"hljs-punctuation\">,<\/span> rate limiting<span class=\"hljs-punctuation\">,<\/span> etc.\n    \u2193\nCaddy forwards request to localhost<span class=\"hljs-punctuation\">:<\/span><span class=\"hljs-number\">3000<\/span> (Node.js)\n    \u2193\nNode.js processes the request\n    \u2193\nNode.js sends response back to Caddy\n    \u2193\nCaddy sends response to user's browser\n    \u2193\nUser sees the result\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\">Key benefits of this architecture:<\/p>\n<ul class=\"list-disc ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Node.js doesn&#8217;t handle HTTPS directly (Caddy does)<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Node.js doesn&#8217;t expose anything to the internet<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">If Node.js crashes, Caddy gracefully returns an error<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">If Node.js is updating, Caddy can wait for it to restart<\/p>\n<\/li>\n<\/ul>\n<hr \/>\n<h2 class=\"text-3xl font-semibold mt-14 mb-8 \">Prerequisites: What You Need Before Starting<\/h2>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Server Requirements<\/h3>\n<table>\n<thead>\n<tr>\n<th>Requirement<\/th>\n<th>Details<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong>Operating System<\/strong><\/td>\n<td>Ubuntu 20.04 LTS or later (most common)<\/td>\n<\/tr>\n<tr>\n<td><strong>VPS Provider<\/strong><\/td>\n<td>Any cloud provider (AWS, DigitalOcean, Linode, etc.)<\/td>\n<\/tr>\n<tr>\n<td><strong>RAM<\/strong><\/td>\n<td>Minimum 512 MB (1 GB recommended for production)<\/td>\n<\/tr>\n<tr>\n<td><strong>CPU<\/strong><\/td>\n<td>Minimum 1 core (2+ cores recommended)<\/td>\n<\/tr>\n<tr>\n<td><strong>Storage<\/strong><\/td>\n<td>Minimum 10 GB (depends on app and data)<\/td>\n<\/tr>\n<tr>\n<td><strong>Network<\/strong><\/td>\n<td>Public IP address with ports 80 and 443 open<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Software Requirements<\/h3>\n<table>\n<thead>\n<tr>\n<th>Software<\/th>\n<th>Version<\/th>\n<th>Purpose<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong>Node.js<\/strong><\/td>\n<td>16.x or later<\/td>\n<td>Run your application<\/td>\n<\/tr>\n<tr>\n<td><strong>npm or yarn<\/strong><\/td>\n<td>Latest<\/td>\n<td>Manage Node.js dependencies<\/td>\n<\/tr>\n<tr>\n<td><strong>PM2<\/strong><\/td>\n<td>Latest<\/td>\n<td>Process manager for Node.js<\/td>\n<\/tr>\n<tr>\n<td><strong>Caddy<\/strong><\/td>\n<td>2.x<\/td>\n<td>Reverse proxy and web server<\/td>\n<\/tr>\n<tr>\n<td><strong>curl<\/strong><\/td>\n<td>Any<\/td>\n<td>Test commands<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Domain Requirements<\/h3>\n<ul class=\"list-disc ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">A domain name (e.g., example.com)<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Domain must be registered and pointing to your VPS IP address<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">How to check: <code class=\"break-words rounded bg-[#24292E] px-2 py-1 text-[#EEEEEE]\">nslookup example.com<\/code> should return your VPS&#8217;s IP<\/p>\n<\/li>\n<\/ul>\n<p class=\" text-lg my-6\"><strong>To point your domain to the VPS:<\/strong><\/p>\n<ol class=\"list-decimal ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Get your VPS IP address (shown in your VPS provider&#8217;s dashboard)<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Go to your domain registrar (GoDaddy, Namecheap, etc.)<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Find &#8220;DNS Settings&#8221; or &#8220;Nameservers&#8221;<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Add an A record pointing to your VPS IP<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Wait 5-30 minutes for DNS to propagate<\/p>\n<\/li>\n<\/ol>\n<p class=\" text-lg my-6\"><strong>To verify DNS is working:<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Should return your VPS IP<\/span>\nnslookup example.com\n\n<span class=\"hljs-comment\"># Should show your server<\/span>\ndig example.com\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Access Requirements<\/h3>\n<p class=\" text-lg my-6\">You need one of:<\/p>\n<ul class=\"list-disc ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">SSH access to the VPS (with sudo privileges)<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Root access to the VPS<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Admin\/sudo user on the VPS<\/p>\n<\/li>\n<\/ul>\n<hr \/>\n<h2 class=\"text-3xl font-semibold mt-14 mb-8 \">Step 1: Prepare Your Node.js Application<\/h2>\n<p class=\" text-lg my-6\">Before installing Caddy, ensure your Node.js app is ready.<\/p>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Create a Simple Node.js App (if you don&#8217;t have one)<\/h3>\n<p class=\" text-lg my-6\">Create a basic Express server for testing:<\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Create project directory<\/span>\n<span class=\"hljs-built_in\">mkdir<\/span> my-node-app\n<span class=\"hljs-built_in\">cd<\/span> my-node-app\n\n<span class=\"hljs-comment\"># Initialize Node.js project<\/span>\nnpm init -y\n\n<span class=\"hljs-comment\"># Install Express<\/span>\nnpm install express\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\">Create <code class=\"break-words rounded bg-[#24292E] px-2 py-1 text-[#EEEEEE]\">server.js<\/code>:<\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-javascript whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-keyword\">const<\/span> express = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">'express'<\/span>);\n<span class=\"hljs-keyword\">const<\/span> app = <span class=\"hljs-title function_\">express<\/span>();\n<span class=\"hljs-keyword\">const<\/span> <span class=\"hljs-variable constant_\">PORT<\/span> = process.<span class=\"hljs-property\">env<\/span>.<span class=\"hljs-property\">PORT<\/span> || <span class=\"hljs-number\">3000<\/span>;\n\napp.<span class=\"hljs-title function_\">get<\/span>(<span class=\"hljs-string\">'\/'<\/span>, <span class=\"hljs-function\">(<span class=\"hljs-params\">req, res<\/span>) =&gt;<\/span> {\n    res.<span class=\"hljs-title function_\">json<\/span>({ \n        <span class=\"hljs-attr\">message<\/span>: <span class=\"hljs-string\">'Hello from Node.js behind Caddy!'<\/span>,\n        <span class=\"hljs-attr\">timestamp<\/span>: <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-title class_\">Date<\/span>().<span class=\"hljs-title function_\">toISOString<\/span>()\n    });\n});\n\napp.<span class=\"hljs-title function_\">get<\/span>(<span class=\"hljs-string\">'\/health'<\/span>, <span class=\"hljs-function\">(<span class=\"hljs-params\">req, res<\/span>) =&gt;<\/span> {\n    res.<span class=\"hljs-title function_\">json<\/span>({ <span class=\"hljs-attr\">status<\/span>: <span class=\"hljs-string\">'healthy'<\/span> });\n});\n\napp.<span class=\"hljs-title function_\">listen<\/span>(<span class=\"hljs-variable constant_\">PORT<\/span>, <span class=\"hljs-function\">() =&gt;<\/span> {\n    <span class=\"hljs-variable language_\">console<\/span>.<span class=\"hljs-title function_\">log<\/span>(<span class=\"hljs-string\">`\u2713 Server running on http:\/\/localhost:<span class=\"hljs-subst\">${PORT}<\/span>`<\/span>);\n});\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\">Test locally:<\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\">node server.js\n<span class=\"hljs-comment\"># Output: \u2713 Server running on http:\/\/localhost:3000<\/span>\n\n<span class=\"hljs-comment\"># In another terminal:<\/span>\ncurl http:\/\/localhost:3000\n<span class=\"hljs-comment\"># Output: {\"message\":\"Hello from Node.js behind Caddy!\",\"timestamp\":\"2025-01-15...\"}<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Upload to Server<\/h3>\n<p class=\" text-lg my-6\">Transfer your app to the VPS:<\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># From your local machine<\/span>\n<span class=\"hljs-comment\"># Option 1: Using scp (copy files)<\/span>\nscp -r my-node-app user@your-vps-ip:\/home\/user\/\n\n<span class=\"hljs-comment\"># Option 2: Using git<\/span>\n<span class=\"hljs-comment\"># Push to GitHub, then git clone on the VPS<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\">Install dependencies on the server:<\/p>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Method 1: NodeSource PPA (Recommended for Production)[1]<\/h3>\n<p class=\" text-lg my-6\">This is the industry-standard approach for production VPS deployments, providing the latest LTS version with security updates.<\/p>\n<p class=\" text-lg my-6\"><strong>Step 1: Update System and Install Dependencies<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-built_in\">sudo<\/span> apt update &amp;&amp; <span class=\"hljs-built_in\">sudo<\/span> apt upgrade -y\n<span class=\"hljs-built_in\">sudo<\/span> apt install -y curl\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\"><strong>Step 2: Add NodeSource Repository<\/strong><\/p>\n<p class=\" text-lg my-6\">For Node.js v24 LTS (current stable):<\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\">curl -fsSL https:\/\/deb.nodesource.com\/setup_24.x | <span class=\"hljs-built_in\">sudo<\/span> -E bash -\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\">To verify the script before running (security best practice):<\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\">curl -fsSL https:\/\/deb.nodesource.com\/setup_24.x -o nodesource_setup.sh\nnano nodesource_setup.sh  <span class=\"hljs-comment\"># Review contents<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> bash nodesource_setup.sh\n<span class=\"hljs-built_in\">rm<\/span> nodesource_setup.sh\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\"><strong>Step 3: Install Node.js and npm<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-built_in\">sudo<\/span> apt install -y nodejs build-essential\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\">The <code class=\"break-words rounded bg-[#24292E] px-2 py-1 text-[#EEEEEE]\">build-essential<\/code> package is critical\u2014it installs gcc, g++, and make, required for npm packages that compile native code (bcrypt, sqlite3, etc.).<\/p>\n<p class=\" text-lg my-6\"><strong>Step 4: Verify Installation<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\">node --version    <span class=\"hljs-comment\"># Should show v24.x.x<\/span>\nnpm --version     <span class=\"hljs-comment\"># Should show 11.x.x<\/span>\nnpm list -g pm2   <span class=\"hljs-comment\"># Check if PM2 is installed globally<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<hr \/>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Method 2: NVM (Node Version Manager)[2][3]<\/h3>\n<p class=\" text-lg my-6\"><strong>Ideal for developers needing multiple Node.js versions or development-focused workflows.<\/strong><\/p>\n<p class=\" text-lg my-6\"><strong>Step 1: Install NVM<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\">curl -o- https:\/\/raw.githubusercontent.com\/nvm-sh\/nvm\/v0.39.5\/install.sh | bash\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\"><strong>Step 2: Load NVM into Current Session<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-built_in\">export<\/span> NVM_DIR=<span class=\"hljs-string\">\"<span class=\"hljs-variable\">$HOME<\/span>\/.nvm\"<\/span>\n[ -s <span class=\"hljs-string\">\"<span class=\"hljs-variable\">$NVM_DIR<\/span>\/nvm.sh\"<\/span> ] &amp;&amp; \\. <span class=\"hljs-string\">\"<span class=\"hljs-variable\">$NVM_DIR<\/span>\/nvm.sh\"<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\">Or reopen your terminal.<\/p>\n<p class=\" text-lg my-6\"><strong>Step 3: Verify NVM Installation<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\">nvm --version  <span class=\"hljs-comment\"># Should show v0.39.5 or similar<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\"><strong>Step 4: Install Specific Node.js Version<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\">nvm install node              <span class=\"hljs-comment\"># Latest<\/span>\nnvm install --lts             <span class=\"hljs-comment\"># Latest LTS<\/span>\nnvm install 20                <span class=\"hljs-comment\"># Specific version<\/span>\nnvm list                       <span class=\"hljs-comment\"># Show installed versions<\/span>\nnvm use 20                     <span class=\"hljs-comment\"># Switch to v20<\/span>\nnvm <span class=\"hljs-built_in\">alias<\/span> default 20          <span class=\"hljs-comment\"># Set default version<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\"><strong>Make NVM Persistent (Add to ~\/.bashrc):<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-built_in\">export<\/span> NVM_DIR=<span class=\"hljs-string\">\"<span class=\"hljs-variable\">$HOME<\/span>\/.nvm\"<\/span>\n[ -s <span class=\"hljs-string\">\"<span class=\"hljs-variable\">$NVM_DIR<\/span>\/nvm.sh\"<\/span> ] &amp;&amp; \\. <span class=\"hljs-string\">\"<span class=\"hljs-variable\">$NVM_DIR<\/span>\/nvm.sh\"<\/span>\n[ -s <span class=\"hljs-string\">\"<span class=\"hljs-variable\">$NVM_DIR<\/span>\/bash_completion\"<\/span> ] &amp;&amp; \\. <span class=\"hljs-string\">\"<span class=\"hljs-variable\">$NVM_DIR<\/span>\/bash_completion\"<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\">Then reload:<\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-built_in\">source<\/span> ~\/.bashrc\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\">Then Go To:<\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># On the VPS<\/span>\n<span class=\"hljs-built_in\">cd<\/span> \/home\/user\/my-node-app\nnpm install\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<hr \/>\n<h2 class=\"text-3xl font-semibold mt-14 mb-8 \">Step 2: Run Node.js with PM2 (Process Manager)<\/h2>\n<p class=\" text-lg my-6\"><strong>Why PM2?<\/strong><\/p>\n<p class=\" text-lg my-6\">Running <code class=\"break-words rounded bg-[#24292E] px-2 py-1 text-[#EEEEEE]\">node server.js<\/code> directly has problems:<\/p>\n<ul class=\"list-disc ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">If the app crashes, it stays down<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">If you restart the server, the app doesn&#8217;t auto-start<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">You can&#8217;t easily manage multiple Node.js apps<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">It&#8217;s hard to update code without downtime<\/p>\n<\/li>\n<\/ul>\n<p class=\" text-lg my-6\"><strong>PM2 solves all of these:<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-json whitespace-pre-wrap break-words text-gray-300\">Process Manager (PM2)\n    \u251c\u2500 Monitor app health\n    \u251c\u2500 Auto-restart on crash\n    \u251c\u2500 Start on system boot\n    \u251c\u2500 Manage multiple apps\n    \u251c\u2500 Provide logs\n    \u2514\u2500 Support zero-downtime updates\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Install PM2<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Global installation<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> npm install -g pm2\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Start Your App with PM2<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Navigate to your app directory<\/span>\n<span class=\"hljs-built_in\">cd<\/span> \/home\/user\/my-node-app\n\n<span class=\"hljs-comment\"># Start the app with PM2<\/span>\npm2 start server.js --name <span class=\"hljs-string\">\"node-app\"<\/span>\n\n<span class=\"hljs-comment\"># Output:<\/span>\n<span class=\"hljs-comment\"># \u250c\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510<\/span>\n<span class=\"hljs-comment\"># \u2502 id \u2502 name           \u2502 mode \u2502 \u21ba    \u2502 status \u2502 cpu     \u2502 memory  \u2502<\/span>\n<span class=\"hljs-comment\"># \u251c\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524<\/span>\n<span class=\"hljs-comment\"># \u2502 0  \u2502 node-app       \u2502 fork \u2502 0    \u2502 online \u2502 0%      \u2502 28.2mb  \u2502<\/span>\n<span class=\"hljs-comment\"># \u2514\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\"><strong>What this means:<\/strong><\/p>\n<ul class=\"list-disc ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><code class=\"break-words rounded bg-[#24292E] px-2 py-1 text-[#EEEEEE]\">status: online<\/code> = App is running<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><code class=\"break-words rounded bg-[#24292E] px-2 py-1 text-[#EEEEEE]\">\u21ba 0<\/code> = Haven&#8217;t needed to restart yet<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><code class=\"break-words rounded bg-[#24292E] px-2 py-1 text-[#EEEEEE]\">cpu: 0%<\/code> = Not using much CPU<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><code class=\"break-words rounded bg-[#24292E] px-2 py-1 text-[#EEEEEE]\">memory: 28.2mb<\/code> = Using 28 MB RAM<\/p>\n<\/li>\n<\/ul>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Configure PM2 to Start on Boot<\/h3>\n<p class=\" text-lg my-6\">When you restart the server, you want your app to automatically start:<\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Generate startup script<\/span>\npm2 startup systemd -u user --hp \/home\/user\n\n<span class=\"hljs-comment\"># This outputs a command to run. Copy and paste it:<\/span>\n<span class=\"hljs-comment\"># sudo env PATH=$PATH:\/usr\/bin \/usr\/lib\/node_modules\/pm2\/bin\/pm2 startup systemd -u user --hp \/home\/user<\/span>\n\n<span class=\"hljs-comment\"># Then save the PM2 configuration<\/span>\npm2 save\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\">Test that it&#8217;s saved:<\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Show PM2 configuration<\/span>\npm2 status\n\n<span class=\"hljs-comment\"># Output shows your app listed<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Verify App Runs on Port 3000<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Check if your app is listening on port 3000<\/span>\ncurl http:\/\/localhost:3000\n\n<span class=\"hljs-comment\"># Output: {\"message\":\"Hello from Node.js behind Caddy!\",\"timestamp\":\"...\"}<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">PM2 Common Commands<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># View app status<\/span>\npm2 status\n\n<span class=\"hljs-comment\"># View live logs<\/span>\npm2 logs node-app\n\n<span class=\"hljs-comment\"># Stop the app<\/span>\npm2 stop node-app\n\n<span class=\"hljs-comment\"># Restart the app<\/span>\npm2 restart node-app\n\n<span class=\"hljs-comment\"># Delete the app from PM2<\/span>\npm2 delete node-app\n\n<span class=\"hljs-comment\"># Update app with zero downtime<\/span>\npm2 reload node-app\n\n<span class=\"hljs-comment\"># Monitor CPU\/memory usage<\/span>\npm2 monit\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<hr \/>\n<h2 class=\"text-3xl font-semibold mt-14 mb-8 \">Step 3: Install Caddy on Ubuntu<\/h2>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Add Caddy Repository<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Update package list<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> apt update\n\n<span class=\"hljs-comment\"># Add Caddy's GPG key<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> apt install -y debian-keyring debian-archive-keyring apt-transport-https\n\n<span class=\"hljs-comment\"># Add Caddy repository<\/span>\ncurl -1sLf <span class=\"hljs-string\">'https:\/\/dl.caddy.community\/api\/publish\/deb\/cfg\/gpg\/gpgkey.pub'<\/span> | \\\n  <span class=\"hljs-built_in\">sudo<\/span> gpg --dearmor -o \/usr\/share\/keyrings\/caddy-archive-keyring.gpg\n\n<span class=\"hljs-built_in\">echo<\/span> <span class=\"hljs-string\">\"deb [signed-by=\/usr\/share\/keyrings\/caddy-archive-keyring.gpg] https:\/\/dl.caddy.community\/api\/publish\/deb\/cfg\/stable\/any\/ any main\"<\/span> | \\\n  <span class=\"hljs-built_in\">sudo<\/span> <span class=\"hljs-built_in\">tee<\/span> \/etc\/apt\/sources.list.d\/caddy-stable.list\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Install Caddy<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Update package list<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> apt update\n\n<span class=\"hljs-comment\"># Install Caddy<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> apt install -y caddy\n\n<span class=\"hljs-comment\"># Verify installation<\/span>\ncaddy version\n<span class=\"hljs-comment\"># Output: v2.x.x (specific version number)<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Enable Caddy to Start on Boot<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Enable the Caddy service<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> systemctl <span class=\"hljs-built_in\">enable<\/span> caddy\n\n<span class=\"hljs-comment\"># Start Caddy now<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> systemctl start caddy\n\n<span class=\"hljs-comment\"># Check status<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> systemctl status caddy\n<span class=\"hljs-comment\"># Output: active (running) since...<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<hr \/>\n<h2 class=\"text-3xl font-semibold mt-14 mb-8 \">Step 4: Configure Caddy as a Reverse Proxy<\/h2>\n<p class=\" text-lg my-6\">The Caddyfile is Caddy&#8217;s configuration file. It&#8217;s simple and readable.<\/p>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Locate the Caddyfile<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Default location<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> nano \/etc\/caddy\/Caddyfile\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Basic Configuration<\/h3>\n<p class=\" text-lg my-6\">Replace the existing content with:<\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-json whitespace-pre-wrap break-words text-gray-300\">example.com <span class=\"hljs-punctuation\">{<\/span>\n    reverse_proxy localhost<span class=\"hljs-punctuation\">:<\/span><span class=\"hljs-number\">3000<\/span>\n<span class=\"hljs-punctuation\">}<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\"><strong>What this configuration does:<\/strong><\/p>\n<table>\n<thead>\n<tr>\n<th>Line<\/th>\n<th>Meaning<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code class=\"break-words rounded bg-[#24292E] px-2 py-1 text-[#EEEEEE]\">example.com {<\/code><\/td>\n<td>Listen on this domain<\/td>\n<\/tr>\n<tr>\n<td><code class=\"break-words rounded bg-[#24292E] px-2 py-1 text-[#EEEEEE]\">reverse_proxy localhost:3000<\/code><\/td>\n<td>Forward all requests to Node.js on port 3000<\/td>\n<\/tr>\n<tr>\n<td><code class=\"break-words rounded bg-[#24292E] px-2 py-1 text-[#EEEEEE]\">}<\/code><\/td>\n<td>End domain configuration<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\" text-lg my-6\"><strong>Save the file:<\/strong><\/p>\n<ul class=\"list-disc ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Press <code class=\"break-words rounded bg-[#24292E] px-2 py-1 text-[#EEEEEE]\">Ctrl + O<\/code> to save<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Press <code class=\"break-words rounded bg-[#24292E] px-2 py-1 text-[#EEEEEE]\">Enter<\/code> to confirm<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Press <code class=\"break-words rounded bg-[#24292E] px-2 py-1 text-[#EEEEEE]\">Ctrl + X<\/code> to exit nano<\/p>\n<\/li>\n<\/ul>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Understanding Reverse Proxy Flow<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-json whitespace-pre-wrap break-words text-gray-300\">Client request to https<span class=\"hljs-punctuation\">:<\/span><span class=\"hljs-comment\">\/\/example.com\/api\/users<\/span>\n    \u2193\nCaddy receives at port <span class=\"hljs-number\">443<\/span>\n    \u2193\nCaddy terminates HTTPS\n    \u2193\nCaddy forwards HTTP request to http<span class=\"hljs-punctuation\">:<\/span><span class=\"hljs-comment\">\/\/localhost:3000\/api\/users<\/span>\n    \u2193\nNode.js receives and processes\n    \u2193\nNode.js sends response to Caddy\n    \u2193\nCaddy converts response to HTTPS\n    \u2193\nResponse sent to client\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<hr \/>\n<h2 class=\"text-3xl font-semibold mt-14 mb-8 \">Step 5: Validate Caddy Configuration<\/h2>\n<p class=\" text-lg my-6\">Before reloading, always validate the configuration to catch errors:<\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Validate syntax<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> caddy validate --config \/etc\/caddy\/Caddyfile\n\n<span class=\"hljs-comment\"># Output on success:<\/span>\n<span class=\"hljs-comment\"># Valid configuration<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\">If there are errors, it will tell you the line number and problem.<\/p>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Common Errors<\/h3>\n<p class=\" text-lg my-6\"><strong>Error:<\/strong> <code class=\"break-words rounded bg-[#24292E] px-2 py-1 text-[#EEEEEE]\">missing domain name<\/code><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-json whitespace-pre-wrap break-words text-gray-300\"># \u274c WRONG\nreverse_proxy localhost<span class=\"hljs-punctuation\">:<\/span><span class=\"hljs-number\">3000<\/span>\n\n# \u2705 CORRECT\nexample.com <span class=\"hljs-punctuation\">{<\/span>\n    reverse_proxy localhost<span class=\"hljs-punctuation\">:<\/span><span class=\"hljs-number\">3000<\/span>\n<span class=\"hljs-punctuation\">}<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\"><strong>Error:<\/strong> <code class=\"break-words rounded bg-[#24292E] px-2 py-1 text-[#EEEEEE]\">unmarshal: line 1: unknown directive 'example.com'<\/code><\/p>\n<p class=\" text-lg my-6\">This usually means there&#8217;s a syntax error. Check:<\/p>\n<ul class=\"list-disc ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Are there opening and closing braces <code class=\"break-words rounded bg-[#24292E] px-2 py-1 text-[#EEEEEE]\">{ }<\/code>?<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">No trailing colons after domain name<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Proper indentation<\/p>\n<\/li>\n<\/ul>\n<p class=\" text-lg my-6\"><strong>Error:<\/strong> <code class=\"break-words rounded bg-[#24292E] px-2 py-1 text-[#EEEEEE]\">duplicate site addresses<\/code><\/p>\n<p class=\" text-lg my-6\">You have the same domain listed twice. Check for duplicates.<\/p>\n<hr \/>\n<h2 class=\"text-3xl font-semibold mt-14 mb-8 \">Step 6: Reload Caddy Configuration<\/h2>\n<p class=\" text-lg my-6\">After validation, reload Caddy with the new configuration:<\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Reload without downtime<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> systemctl reload caddy\n\n<span class=\"hljs-comment\"># Check status<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> systemctl status caddy\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\"><strong>Important:<\/strong> <code class=\"break-words rounded bg-[#24292E] px-2 py-1 text-[#EEEEEE]\">reload<\/code> keeps existing connections active while loading the new config. This is different from <code class=\"break-words rounded bg-[#24292E] px-2 py-1 text-[#EEEEEE]\">restart<\/code>, which stops and starts the service.<\/p>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Verify Caddy Is Running<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Check Caddy process<\/span>\nps aux | grep caddy\n<span class=\"hljs-comment\"># Should show caddy process<\/span>\n\n<span class=\"hljs-comment\"># Check listening ports<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> netstat -tlnp | grep caddy\n<span class=\"hljs-comment\"># Should show ports 80 and 443<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<hr \/>\n<h2 class=\"text-3xl font-semibold mt-14 mb-8 \">Step 7: Verify HTTPS Certificate<\/h2>\n<p class=\" text-lg my-6\">Caddy automatically obtains and manages SSL\/TLS certificates using Let&#8217;s Encrypt.<\/p>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Check Certificate Status<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># View certificate information<\/span>\n<span class=\"hljs-built_in\">echo<\/span> | openssl s_client -servername example.com -connect example.com:443\n\n<span class=\"hljs-comment\"># Look for:<\/span>\n<span class=\"hljs-comment\"># - subject=CN=example.com<\/span>\n<span class=\"hljs-comment\"># - Not Before and Not After dates<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">View Caddy&#8217;s Certificate Storage<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Certificates stored here<\/span>\n<span class=\"hljs-built_in\">ls<\/span> -la ~\/.local\/share\/caddy\/certificates\/\n\n<span class=\"hljs-comment\"># If running as a service:<\/span>\n<span class=\"hljs-built_in\">ls<\/span> -la \/root\/.local\/share\/caddy\/certificates\/\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">View Certificate Expiry<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Get expiry date<\/span>\n<span class=\"hljs-built_in\">echo<\/span> | openssl s_client -servername example.com -connect example.com:443 2&gt;\/dev\/null | \\\n  openssl x509 -noout -dates\n\n<span class=\"hljs-comment\"># Output:<\/span>\n<span class=\"hljs-comment\"># notBefore=Jan 15 12:00:00 2025 GMT<\/span>\n<span class=\"hljs-comment\"># notAfter=Apr 15 12:00:00 2025 GMT<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\">Caddy automatically renews certificates 30 days before expiry. You don&#8217;t need to do anything.<\/p>\n<hr \/>\n<h2 class=\"text-3xl font-semibold mt-14 mb-8 \">Step 8: Test Your Setup<\/h2>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Test from Command Line<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Test HTTP (redirects to HTTPS)<\/span>\ncurl -i http:\/\/example.com\n<span class=\"hljs-comment\"># Output: HTTP\/1.1 301 Moved Permanently<\/span>\n<span class=\"hljs-comment\">#         Location: https:\/\/example.com\/<\/span>\n\n<span class=\"hljs-comment\"># Test HTTPS<\/span>\ncurl https:\/\/example.com\n<span class=\"hljs-comment\"># Output: {\"message\":\"Hello from Node.js behind Caddy!\",\"timestamp\":\"...\"}<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Test from Browser<\/h3>\n<p class=\" text-lg my-6\">Open your browser and visit:<\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-json whitespace-pre-wrap break-words text-gray-300\">https<span class=\"hljs-punctuation\">:<\/span><span class=\"hljs-comment\">\/\/example.com<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\">You should see:<\/p>\n<ul class=\"list-disc ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">A green lock icon (HTTPS is working)<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Your Node.js app&#8217;s response<\/p>\n<\/li>\n<\/ul>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Test Specific Routes<\/h3>\n<p class=\" text-lg my-6\">If your Node.js app has multiple routes:<\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Test health check<\/span>\ncurl https:\/\/example.com\/health\n\n<span class=\"hljs-comment\"># Test API<\/span>\ncurl https:\/\/example.com\/api\/endpoint\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Check HTTPS Security<\/h3>\n<p class=\" text-lg my-6\">Visit <a class=\"! !underline\" href=\"https:\/\/www.ssllabs.com\/ssltest\/\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/www.ssllabs.com\/ssltest\/<\/a> and enter your domain. It should get an A or A+ rating.<\/p>\n<hr \/>\n<h2 class=\"text-3xl font-semibold mt-14 mb-8 \">Step 9: View Logs<\/h2>\n<p class=\" text-lg my-6\">Monitor what Caddy is doing:<\/p>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Real-Time Logs<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># View live logs<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> journalctl -u caddy -f\n\n<span class=\"hljs-comment\"># Example output:<\/span>\n<span class=\"hljs-comment\"># Jan 15 12:30:45 server caddy[1234]: {\"level\":\"info\",\"msg\":\"autosaved config (load returned nil)\"}<\/span>\n<span class=\"hljs-comment\"># Jan 15 12:30:46 server caddy[1234]: {\"level\":\"info\",\"msg\":\"Certificates obtained successfully\"}<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">View Past Logs<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Last 50 lines<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> journalctl -u caddy -n 50\n\n<span class=\"hljs-comment\"># Last 1 hour<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> journalctl -u caddy --since <span class=\"hljs-string\">\"1 hour ago\"<\/span>\n\n<span class=\"hljs-comment\"># By date<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> journalctl -u caddy --since <span class=\"hljs-string\">\"2025-01-15\"<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Filter for Errors<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Show only errors<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> journalctl -u caddy | grep -i error\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<hr \/>\n<h2 class=\"text-3xl font-semibold mt-14 mb-8 \">Complete Working Configuration<\/h2>\n<p class=\" text-lg my-6\">Once you&#8217;ve completed the setup, your configuration should look like:<\/p>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">VPS Setup Summary<\/h3>\n<p class=\" text-lg my-6\"><strong>1. Node.js App<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Location: \/home\/user\/my-node-app\/server.js<\/span>\n<span class=\"hljs-comment\"># Running with PM2 on port 3000<\/span>\n<span class=\"hljs-comment\"># Status: pm2 status<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\"><strong>2. PM2 Process<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Status: online<\/span>\n<span class=\"hljs-comment\"># Auto-restart: enabled<\/span>\n<span class=\"hljs-comment\"># Auto-start on boot: enabled<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\"><strong>3. Caddy Configuration<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-json whitespace-pre-wrap break-words text-gray-300\"># File<span class=\"hljs-punctuation\">:<\/span> \/etc\/caddy\/Caddyfile\nexample.com <span class=\"hljs-punctuation\">{<\/span>\n    reverse_proxy localhost<span class=\"hljs-punctuation\">:<\/span><span class=\"hljs-number\">3000<\/span>\n<span class=\"hljs-punctuation\">}<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\"><strong>4. Services<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Both should be running:<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> systemctl status caddy\n<span class=\"hljs-built_in\">sudo<\/span> systemctl status pm2-user\n\n<span class=\"hljs-comment\"># Both should auto-start on boot<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> systemctl is-enabled caddy\n<span class=\"hljs-built_in\">sudo<\/span> systemctl is-enabled pm2-user\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<hr \/>\n<h2 class=\"text-3xl font-semibold mt-14 mb-8 \">Now You&#8217;re Ready for Advanced Security Features<\/h2>\n<p class=\" text-lg my-6\">Your basic setup is complete! Your Node.js app is:<\/p>\n<ul class=\"list-disc ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">\u2705 Running in production with PM2<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">\u2705 Behind a reverse proxy (Caddy)<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">\u2705 Secured with automatic HTTPS<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">\u2705 Accessible at your domain<\/p>\n<\/li>\n<\/ul>\n<p class=\" text-lg my-6\">Now you can add the advanced security features from the other guides:<\/p>\n<ul class=\"list-disc ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Security headers (prevent XSS, clickjacking)<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">IP filtering (allow\/deny specific IPs)<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Rate limiting (prevent abuse and DDoS)<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Basic authentication (password protect routes)<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Request body limits (prevent large uploads)<\/p>\n<\/li>\n<\/ul>\n<hr \/>\n<h2 class=\"text-3xl font-semibold mt-14 mb-8 \">Troubleshooting Common Issues<\/h2>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Issue 1: &#8220;Connection Refused&#8221; Error<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Error: curl: (7) Failed to connect to example.com port 443: Connection refused<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\"><strong>Possible causes:<\/strong><\/p>\n<ol class=\"list-decimal ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">DNS not propagated yet (wait 5-30 minutes)<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Node.js app not running<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Caddy not running<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Firewall blocking ports 80\/443<\/p>\n<\/li>\n<\/ol>\n<p class=\" text-lg my-6\"><strong>Solutions:<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Check Node.js is running<\/span>\npm2 status\n\n<span class=\"hljs-comment\"># Check Caddy is running<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> systemctl status caddy\n\n<span class=\"hljs-comment\"># Check ports are listening<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> netstat -tlnp | grep -E <span class=\"hljs-string\">':80|:443'<\/span>\n\n<span class=\"hljs-comment\"># Check firewall (if using UFW)<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> ufw status\n<span class=\"hljs-built_in\">sudo<\/span> ufw allow 80\n<span class=\"hljs-built_in\">sudo<\/span> ufw allow 443\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Issue 2: &#8220;Certificate Problem&#8221; Error<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Error: curl: (60) SSL certificate problem<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\"><strong>Possible causes:<\/strong><\/p>\n<ol class=\"list-decimal ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Certificate not obtained yet (wait 5 minutes)<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Domain not pointing to this server<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Firewall blocking certificate renewal<\/p>\n<\/li>\n<\/ol>\n<p class=\" text-lg my-6\"><strong>Solutions:<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Check DNS points to your server<\/span>\nnslookup example.com\n\n<span class=\"hljs-comment\"># Wait for certificate<\/span>\n<span class=\"hljs-built_in\">sleep<\/span> 30\ncurl https:\/\/example.com\n\n<span class=\"hljs-comment\"># Check Caddy logs for certificate errors<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> journalctl -u caddy | grep -i cert\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Issue 3: &#8220;502 Bad Gateway&#8221;<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Error from browser: 502 Bad Gateway<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\"><strong>Causes:<\/strong> Node.js app is down or not listening on port 3000<\/p>\n<p class=\" text-lg my-6\"><strong>Solutions:<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Check if Node.js is running<\/span>\npm2 status\n<span class=\"hljs-comment\"># Should show \"online\" status<\/span>\n\n<span class=\"hljs-comment\"># If not running, restart it<\/span>\npm2 restart node-app\n\n<span class=\"hljs-comment\"># Check if it's listening on port 3000<\/span>\ncurl http:\/\/localhost:3000\n\n<span class=\"hljs-comment\"># View Node.js logs<\/span>\npm2 logs node-app\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Issue 4: &#8220;Port Already in Use&#8221;<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Error: listen EADDRINUSE :::3000<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\"><strong>Something else is using port 3000<\/strong><\/p>\n<p class=\" text-lg my-6\"><strong>Solutions:<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Find what's using port 3000<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> lsof -i :3000\n\n<span class=\"hljs-comment\"># Kill the process<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> <span class=\"hljs-built_in\">kill<\/span> -9 &lt;PID&gt;\n\n<span class=\"hljs-comment\"># Or use a different port in your Node.js app<\/span>\n<span class=\"hljs-comment\"># Change: const PORT = 3001;<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Issue 5: Caddy Won&#8217;t Start<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Error: Unit caddy.service failed to load<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<p class=\" text-lg my-6\"><strong>Solutions:<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Check if Caddyfile is valid<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> caddy validate --config \/etc\/caddy\/Caddyfile\n\n<span class=\"hljs-comment\"># Check permissions on Caddyfile<\/span>\n<span class=\"hljs-built_in\">ls<\/span> -la \/etc\/caddy\/Caddyfile\n<span class=\"hljs-comment\"># Should be readable by caddy user<\/span>\n\n<span class=\"hljs-comment\"># Check Caddy logs<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> journalctl -u caddy -n 50\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<hr \/>\n<h2 class=\"text-3xl font-semibold mt-14 mb-8 \">Maintenance and Updates<\/h2>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Update Node.js App (Zero Downtime)<\/h3>\n<p class=\" text-lg my-6\">When you update your code without stopping the server:<\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Pull latest code<\/span>\n<span class=\"hljs-built_in\">cd<\/span> \/home\/user\/my-node-app\ngit pull\n\n<span class=\"hljs-comment\"># Install new dependencies<\/span>\nnpm install\n\n<span class=\"hljs-comment\"># Reload Node.js with zero downtime<\/span>\npm2 reload node-app\n\n<span class=\"hljs-comment\"># Verify it's running<\/span>\npm2 status\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Update Caddy<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Update package list<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> apt update\n\n<span class=\"hljs-comment\"># Update Caddy<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> apt upgrade caddy\n\n<span class=\"hljs-comment\"># Caddy reloads automatically with zero downtime<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Update PM2<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Update globally<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> npm install -g pm2\n\n<span class=\"hljs-comment\"># Restart PM2<\/span>\npm2 <span class=\"hljs-built_in\">kill<\/span>\npm2 startup systemd -u user --hp \/home\/user\npm2 save\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<h3 class=\"text-2xl mt-10 mb-4 font-bold \">Monitor Server Resources<\/h3>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># View real-time CPU and memory<\/span>\npm2 monit\n\n<span class=\"hljs-comment\"># View one-time snapshot<\/span>\npm2 status\n\n<span class=\"hljs-comment\"># View disk space<\/span>\n<span class=\"hljs-built_in\">df<\/span> -h\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<hr \/>\n<h2 class=\"text-3xl font-semibold mt-14 mb-8 \">Security Best Practices for This Setup<\/h2>\n<ol class=\"list-decimal ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>Keep system updated:<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-built_in\">sudo<\/span> apt update &amp;&amp; <span class=\"hljs-built_in\">sudo<\/span> apt upgrade -y\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>Use strong passwords:<\/strong><\/p>\n<ul class=\"list-disc ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Change your VPS root password<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Don&#8217;t use default credentials<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>Enable firewall:<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-built_in\">sudo<\/span> ufw <span class=\"hljs-built_in\">enable<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> ufw allow 22\/tcp    <span class=\"hljs-comment\"># SSH<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> ufw allow 80\/tcp    <span class=\"hljs-comment\"># HTTP<\/span>\n<span class=\"hljs-built_in\">sudo<\/span> ufw allow 443\/tcp   <span class=\"hljs-comment\"># HTTPS<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>Monitor your application:<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Check logs regularly<\/span>\npm2 logs\n<span class=\"hljs-built_in\">sudo<\/span> journalctl -u caddy -f\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>Backup your data:<\/strong><\/p>\n<ul class=\"list-disc ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Regular backups of your VPS<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\">Use your provider&#8217;s snapshot feature<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>Use environment variables for secrets:<\/strong><\/p>\n<div class=\"relative group\">\n<pre class=\"relative bg-[#1a1a1a] border border-gray-700 rounded-lg overflow-x-auto my-8 p-6\"><code class=\"hljs language-bash whitespace-pre-wrap break-words text-gray-300\"><span class=\"hljs-comment\"># Don't commit secrets to git!<\/span>\n<span class=\"hljs-built_in\">export<\/span> DATABASE_URL=<span class=\"hljs-string\">\"postgres:\/\/...\"<\/span>\n<span class=\"hljs-built_in\">export<\/span> API_KEY=<span class=\"hljs-string\">\"secret-key\"<\/span>\npm2 start server.js --name <span class=\"hljs-string\">\"node-app\"<\/span>\n<\/code><\/pre>\n<p><button class=\"absolute top-4 cursor-pointer right-4 p-2 rounded-md bg-[#24292e] hover:bg-gray-700 border border-gray-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200\" title=\"Copy code\"><\/button><\/div>\n<\/li>\n<\/ol>\n<hr \/>\n<h2 class=\"text-3xl font-semibold mt-14 mb-8 \">Key Takeaways<\/h2>\n<ol class=\"list-decimal ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>PM2 keeps your Node.js app running<\/strong> reliably in production with auto-restart and auto-start features.<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>Caddy handles HTTPS automatically<\/strong> using Let&#8217;s Encrypt, no manual certificate management needed.<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>Reverse proxy architecture separates concerns<\/strong> &#8211; Caddy handles web serving, Node.js focuses on business logic.<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>Always validate before reloading<\/strong> &#8211; Check your configuration syntax to avoid downtime.<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>Monitor your setup<\/strong> &#8211; Check logs and process status regularly to catch issues early.<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>This is the foundation<\/strong> &#8211; Once this is working, you can add all the advanced security features from the other guides.<\/p>\n<\/li>\n<\/ol>\n<hr \/>\n<h2 class=\"text-3xl font-semibold mt-14 mb-8 \">Next Steps<\/h2>\n<p class=\" text-lg my-6\">Now that your basic setup is working, you can:<\/p>\n<ol class=\"list-decimal ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>Add Security Headers<\/strong> &#8211; Protect against XSS and other attacks<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>Enable Rate Limiting<\/strong> &#8211; Prevent abuse and DDoS<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>Add IP Filtering<\/strong> &#8211; Allow\/deny specific networks<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>Implement Basic Auth<\/strong> &#8211; Password protect sensitive routes<\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><strong>Limit Upload Sizes<\/strong> &#8211; Prevent disk exhaustion<\/p>\n<\/li>\n<\/ol>\n<p class=\" text-lg my-6\">All of these are simple additions to your Caddyfile!<\/p>\n<hr \/>\n<h2 class=\"text-3xl font-semibold mt-14 mb-8 \">Further Reading<\/h2>\n<ul class=\"list-disc ml-6 my-6\">\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><a class=\"! !underline\" href=\"https:\/\/caddyserver.com\/docs\/\" target=\"_blank\" rel=\"noopener noreferrer\">Caddy Official Documentation<\/a><\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><a class=\"! !underline\" href=\"https:\/\/pm2.keymetrics.io\/docs\/usage\/quick-start\/\" target=\"_blank\" rel=\"noopener noreferrer\">PM2 Getting Started<\/a><\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><a class=\"! !underline\" href=\"https:\/\/nodejs.org\/en\/docs\/guides\/nodejs-performance\/\" target=\"_blank\" rel=\"noopener noreferrer\">Node.js Best Practices<\/a><\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><a class=\"! !underline\" href=\"https:\/\/letsencrypt.org\/how-it-works\/\" target=\"_blank\" rel=\"noopener noreferrer\">Let&#8217;s Encrypt for HTTPS<\/a><\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><a class=\"! !underline\" href=\"https:\/\/www.nginx.com\/resources\/glossary\/reverse-proxy-server\/\" target=\"_blank\" rel=\"noopener noreferrer\">Reverse Proxy Architecture<\/a><\/p>\n<\/li>\n<li class=\" text-lg my-2\">\n<p class=\" text-lg my-6\"><a class=\"! !underline\" href=\"https:\/\/ubuntu.com\/server\/docs\" target=\"_blank\" rel=\"noopener noreferrer\">Ubuntu Server Guide<\/a><\/p>\n<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Introduction: When you deploy a Node.js application to production, you face several challenges: Port Access: Node.js apps run on high ports (3000, 3001, etc.) that require special permissions. HTTP\/HTTPS use ports 80 and 443, which only the root user can access directly. HTTPS Management: Setting up SSL\/TLS certificates manually is complex. You need to obtain&#8230;<\/p>\n","protected":false},"author":1,"featured_media":4751,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[212,213],"tags":[227,216,214,218,220],"class_list":["post-4928","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops-infrastructure","category-software-development","tag-backend","tag-expressjs","tag-mern","tag-nodejs","tag-web","th-blog blog-single has-post-thumbnail"],"_links":{"self":[{"href":"https:\/\/www.softcolon.com\/blogs\/wp-json\/wp\/v2\/posts\/4928","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.softcolon.com\/blogs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.softcolon.com\/blogs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.softcolon.com\/blogs\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.softcolon.com\/blogs\/wp-json\/wp\/v2\/comments?post=4928"}],"version-history":[{"count":2,"href":"https:\/\/www.softcolon.com\/blogs\/wp-json\/wp\/v2\/posts\/4928\/revisions"}],"predecessor-version":[{"id":4930,"href":"https:\/\/www.softcolon.com\/blogs\/wp-json\/wp\/v2\/posts\/4928\/revisions\/4930"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.softcolon.com\/blogs\/wp-json\/wp\/v2\/media\/4751"}],"wp:attachment":[{"href":"https:\/\/www.softcolon.com\/blogs\/wp-json\/wp\/v2\/media?parent=4928"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.softcolon.com\/blogs\/wp-json\/wp\/v2\/categories?post=4928"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.softcolon.com\/blogs\/wp-json\/wp\/v2\/tags?post=4928"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}