Configure Environment Variables in Node.js on Hostinger

by Fahim

Hardcoding your database credentials and third-party API tokens directly into Git will bite you eventually. Push an app to Hostinger with committed secrets or missing runtime values, and you will either watch the build crash on boot or accidentally expose production keys to anyone who inspects your repo.

Here is how to set up environment variables properly for a Node.js app on Hostinger. I’ll walk through local .env hygiene, locking down file permissions, injecting system-level keys through hPanel, and handling PM2 runtime variables on a VPS without leaking credentials.

Terminal screen showing environment variables configuration for a Node.js app on Hostinger
Terminal screen showing environment variables configuration for a Node.js app on Hostinger

The Architecture: How Hostinger Exposes Environment Variables

Hostinger runs Node.js applications in two fundamentally different ways: through their managed Cloud/Web hPanel runner, or directly on a Linux VPS using PM2 and systemd. Each environment handles process.env differently, and mixing up their behaviors causes most failed deployments.

On Hostinger Cloud and Web shared plans, your app sits behind an Nginx reverse proxy running Phusion Passenger or CloudLinux Node Selector. These platforms pull environment variables from two sources: custom directives injected in the dashboard and your root .env file.

On a VPS, you have total control. Your process manager (typically PM2) injects variables straight into the Node worker process at boot. If you already followed my guide to deploy a Node.js app on Hostinger with Namecheap DNS, you already have an active project directory ready for this configuration.

Step 1: Set Up Local and Production .env Files

Keep your secrets out of source control by default. Never paste live database passwords or payment webhook secrets directly into your application code.

Start by creating a template file named .env.example in your repo. This lets anyone pulling your code know exactly what keys the app expects without exposing actual secrets.

NODE_ENV=production
PORT=3000
DATABASE_URL=mysql://u123456789_user:password@127.0.0.1:3306/u123456789_db
JWT_SECRET=replace_this_with_a_64_char_random_string
REDIS_HOST=127.0.0.1
REDIS_PORT=6379

Add a rule to your .gitignore file right away so you don’t accidentally stage and commit your real .env file.

# .gitignore
node_modules/
.env
.env.local
.env.production
*.log

Once you deploy to Hostinger, create the real .env file manually in your application root folder. You can do this through the hPanel File Manager or via SSH using nano .env.

Step 2: Load Variables in Modern Node.js

If you run Node.js v20.6.0 or newer, Node natively loads .env files without external packages using the --env-file flag. Check the official Node.js process.env documentation for runtime specifics.

If you’re on an older LTS release or prefer programmatic loading within your code, install the battle-tested dotenv package in your project root.

npm install dotenv
Import and call dotenv at the absolute top of your entry script (usually server.js, app.js, or index.js) before importing anything else.
import dotenv from 'dotenv';
dotenv.config(); import express from 'express'; const app = express();
const port = process.env.PORT || 3000;
const dbUrl = process.env.DATABASE_URL; if (!dbUrl) { console.error('FATAL: DATABASE_URL is not set.'); process.exit(1);
} app.get('/', (req, res) => { res.json({ status: 'online', environment: process.env.NODE_ENV || 'development' });
}); app.listen(port, () => { console.log(`Server listening on port ${port}`);
});

If you place dotenv.config() after other internal module imports, you will run into silent bugs. Any imported module that reads process.env on load will evaluate before dotenv has actually populated the memory space.

Step 3: Inject Variables in Hostinger hPanel

If you deploy a Node project through Hostinger’s managed dashboard, Passenger spawns the process for you. You can set production variables directly through hPanel without editing files on the server.

  1. Log in to your Hostinger hPanel.
  2. Go to Websites > Dashboard > Node.js.
  3. Locate your app and click Edit.
  4. Scroll down to Environment Variables.
  5. Add your keys and values (like NODE_ENV = production).
  6. Hit Save, then click Restart.

If you’re routing traffic from an isolated project—like when you point a Namecheap subdomain to Hostinger—make sure you isolate that subdomain app’s variables so you don’t overwrite config values used by your main site.

Step 4: Configure PM2 for Hostinger VPS Deployments

When running Node on a Hostinger VPS, PM2 is the best way to handle process restarts and cluster management. Instead of passing inline CLI flags during deployment, manage everything with an ecosystem file.

Drop an ecosystem.config.cjs file into your project root.

module.exports = { apps: [ { name: 'api-service', script: './server.js', instances: 'max', exec_mode: 'cluster', env: { NODE_ENV: 'development', PORT: 3000 }, env_production: { NODE_ENV: 'production', PORT: 8080, DATABASE_URL: 'mysql://vps_user:secretpass@127.0.0.1:3306/prod_db' } } ]
};

Start or reload your app using the --env flag so PM2 injects the production block.

pm2 start ecosystem.config.cjs --env production
pm2 save

Running pm2 save is critical here. It writes your running process configuration to disk so your variables and processes survive server reboots and kernel upgrades.

What I Broke: The Passenger PORT and Path Traversal Trap

The first time I deployed an Express API to Hostinger’s managed Node runner, the server refused to stay up. It kept throwing EADDRINUSE or spitting back 503 Gateway Timeouts. The logs showed Express trying to bind to port 3000, which Hostinger immediately killed.

Hostinger’s Phusion Passenger setup manages socket listeners behind Nginx. It populates process.env.PORT automatically with either a custom internal port string or a Unix domain socket path. A fallback setup like this breaks instantly:

// DON'T DO THIS:
const port = 3000;
app.listen(port);

Bind directly to process.env.PORT without wrapping it in parseInt(). If Passenger assigns a Unix domain socket path, parsing it as an integer turns it into NaN and crashes the server:

// DO THIS:
const port = process.env.PORT || 3000;
app.listen(port, () => { console.log(`Server bound to ${port}`);
});

Path resolution was another trap. When Passenger starts your process, its working directory (process.cwd()) might default to your user root rather than your deployed application folder. Force dotenv to look explicitly relative to your script’s directory:

import path from 'path';
import { fileURLToPath } from 'url';
import dotenv from 'dotenv'; const __dirname = path.dirname(fileURLToPath(import.meta.url));
dotenv.config({ path: path.resolve(__dirname, '.env') });

Security: Restrict .env File Permissions on Hostinger

On Hostinger shared hosting, app files often sit right next to or inside public_html. An unpatched web server configuration or misconfigured rewrite rule could leave your raw .env file accessible over a standard browser request.

First, lock down the Linux permissions over SSH so only your account user can read the file.

chmod 600 /home/u123456789/domains/example.com/app/.env

Second, if your app root lives anywhere near public_html, add a rule to your .htaccess file to explicitly block direct HTTP access.

 Order allow,deny Deny from all

If you’re also running a CMS or external store on the same server, make sure to read my breakdown on how to configure Redis Object Cache on Hostinger so your cache credentials don’t collide with your Node services.

Create a Sanitized Healthcheck Endpoint

After deploying, verify that your runtime variables are active without exposing raw secrets to the public. Never dump process.env straight into a JSON response.

Build a small status endpoint that returns boolean checks for critical keys instead of the values themselves.

app.get('/health', (req, res) => { const requiredVars = ['DATABASE_URL', 'JWT_SECRET', 'NODE_ENV']; const missing = requiredVars.filter((key) => !process.env[key]); if (missing.length > 0) { return res.status(500).json({ status: 'error', message: 'Missing configuration', missingVars: missing }); } res.json({ status: 'healthy', nodeVersion: process.version, uptime: Math.floor(process.uptime()), environment: process.env.NODE_ENV });
});

Hit the endpoint from your terminal to confirm everything loaded:

curl -i https://example.com/health

A clean 200 OK response with "status": "healthy" confirms that your configuration loaded successfully and is ready for production traffic.

Frequently Asked Questions

Can I use Node.js native –env-file on Hostinger?

Yes, as long as your container or VPS runs Node.js 20.6.0 or higher. You can update the start command in your package.json to node --env-file=.env server.js.

Why does my app still read old environment variables after editing .env?

Node reads environment variables into memory once when the process starts. If you modify your .env file or update fields in hPanel, you have to hit Restart in the hPanel Node dashboard or run pm2 reload all over SSH for the changes to register.

Where should I store database passwords on Hostinger?

Keep database credentials in a root .env file with permissions set to 600, or add them directly through Hostinger’s hPanel Environment Variables interface. Never commit credentials to Git.

Does changing the PORT variable in .env work on Hostinger shared hosting?

No. On shared or cloud plans using Passenger, Hostinger manages the listener socket dynamically. Your app code must listen on process.env.PORT, but you cannot dictate the assigned external port number yourself.

Next Steps

Now that your secrets and database strings are secured, test your end-to-end routing. If you run into DNS resolution bugs or need to connect this backend to a separate client app, check out my tutorial on pointing a root domain to a CNAME with Namecheap.

all_in_one_marketing_tool