{"id":3944,"date":"2026-01-01T21:51:59","date_gmt":"2026-01-01T18:51:59","guid":{"rendered":"https:\/\/www.dchost.com\/blog\/managing-env-files-and-secrets-on-a-vps-safely\/"},"modified":"2026-01-01T21:51:59","modified_gmt":"2026-01-01T18:51:59","slug":"managing-env-files-and-secrets-on-a-vps-safely","status":"publish","type":"post","link":"https:\/\/www.dchost.com\/blog\/en\/managing-env-files-and-secrets-on-a-vps-safely\/","title":{"rendered":"Managing .env Files and Secrets on a VPS Safely"},"content":{"rendered":"<div class=\"dchost-blog-content-wrapper\"><p>API keys, database passwords, SMTP credentials, license keys, third-party webhooks\u2026 almost every modern application running on a <a href=\"https:\/\/www.dchost.com\/vps\">VPS<\/a> depends on sensitive configuration values. Most teams put these values into <strong>.env files<\/strong> or environment variables, but the way those secrets are stored on the server often gets far less attention than the application code itself. As a result, we routinely see projects where .env files are world-readable, committed to Git, sitting inside the public web root, or sprinkled across backup archives without any plan. In this guide, we will walk through practical, real-world patterns for managing .env files and secrets safely on a VPS, based on what we do and review with our own customers at dchost.com.<\/p>\n<p>We will focus on Linux VPS servers (Ubuntu, Debian, AlmaLinux, etc.) and common stacks like PHP\/Laravel, WordPress and Node.js, but the principles apply broadly. You will learn how to structure .env files, where to store them on disk, which permissions to use, how to integrate with systemd, how to handle CI\/CD and backups, and when it makes sense to move beyond plain .env files to encrypted secret management.<\/p>\n<div id=\"toc_container\" class=\"toc_transparent no_bullets\"><p class=\"toc_title\">\u0130&ccedil;indekiler<\/p><ul class=\"toc_list\"><li><a href=\"#What_env_Files_and_Secrets_Really_Are_and_Why_They_Matter_on_a_VPS\"><span class=\"toc_number toc_depth_1\">1<\/span> What .env Files and Secrets Really Are (and Why They Matter on a VPS)<\/a><\/li><li><a href=\"#Common_env_Mistakes_We_Keep_Seeing_on_VPS_Servers\"><span class=\"toc_number toc_depth_1\">2<\/span> Common .env Mistakes We Keep Seeing on VPS Servers<\/a><ul><li><a href=\"#1_env_Files_Inside_the_Public_Web_Root\"><span class=\"toc_number toc_depth_2\">2.1<\/span> 1. .env Files Inside the Public Web Root<\/a><\/li><li><a href=\"#2_env_Committed_to_Git_Repositories\"><span class=\"toc_number toc_depth_2\">2.2<\/span> 2. .env Committed to Git Repositories<\/a><\/li><li><a href=\"#3_Overly_Permissive_File_Permissions\"><span class=\"toc_number toc_depth_2\">2.3<\/span> 3. Overly Permissive File Permissions<\/a><\/li><li><a href=\"#4_Secrets_Leaking_into_Logs_and_Debug_Pages\"><span class=\"toc_number toc_depth_2\">2.4<\/span> 4. Secrets Leaking into Logs and Debug Pages<\/a><\/li><li><a href=\"#5_Backups_and_Snapshots_Without_a_Secret_Strategy\"><span class=\"toc_number toc_depth_2\">2.5<\/span> 5. Backups and Snapshots Without a Secret Strategy<\/a><\/li><\/ul><\/li><li><a href=\"#Designing_a_Secure_env_Layout_on_a_VPS\"><span class=\"toc_number toc_depth_1\">3<\/span> Designing a Secure .env Layout on a VPS<\/a><ul><li><a href=\"#1_Separate_Code_from_Configuration\"><span class=\"toc_number toc_depth_2\">3.1<\/span> 1. Separate Code from Configuration<\/a><\/li><li><a href=\"#2_Place_env_Outside_the_Public_Web_Root\"><span class=\"toc_number toc_depth_2\">3.2<\/span> 2. Place .env Outside the Public Web Root<\/a><\/li><li><a href=\"#3_Use_a_Dedicated_Unix_User\"><span class=\"toc_number toc_depth_2\">3.3<\/span> 3. Use a Dedicated Unix User<\/a><\/li><li><a href=\"#4_Lock_Down_File_Permissions_Correctly\"><span class=\"toc_number toc_depth_2\">3.4<\/span> 4. Lock Down File Permissions Correctly<\/a><\/li><li><a href=\"#5_Decide_Environment_File_vs_Native_Environment_Variables\"><span class=\"toc_number toc_depth_2\">3.5<\/span> 5. Decide: Environment File vs Native Environment Variables<\/a><\/li><\/ul><\/li><li><a href=\"#Step-by-Step_Locking_Down_env_on_an_UbuntuDebian_VPS\"><span class=\"toc_number toc_depth_1\">4<\/span> Step-by-Step: Locking Down .env on an Ubuntu\/Debian VPS<\/a><ul><li><a href=\"#1_Create_an_Application_User\"><span class=\"toc_number toc_depth_2\">4.1<\/span> 1. Create an Application User<\/a><\/li><li><a href=\"#2_Create_a_Shared_Configuration_Directory\"><span class=\"toc_number toc_depth_2\">4.2<\/span> 2. Create a Shared Configuration Directory<\/a><\/li><li><a href=\"#3_Create_the_env_File_Safely\"><span class=\"toc_number toc_depth_2\">4.3<\/span> 3. Create the .env File Safely<\/a><\/li><li><a href=\"#4_Configure_Your_Application_to_Use_This_Path\"><span class=\"toc_number toc_depth_2\">4.4<\/span> 4. Configure Your Application to Use This Path<\/a><\/li><li><a href=\"#5_Use_systemd_to_Run_the_App_Under_the_Right_User\"><span class=\"toc_number toc_depth_2\">4.5<\/span> 5. Use systemd to Run the App Under the Right User<\/a><\/li><\/ul><\/li><li><a href=\"#Framework-Specific_Notes_Laravel_WordPress_and_Nodejs\"><span class=\"toc_number toc_depth_1\">5<\/span> Framework-Specific Notes: Laravel, WordPress and Node.js<\/a><ul><li><a href=\"#Laravel_and_Other_Modern_PHP_Frameworks\"><span class=\"toc_number toc_depth_2\">5.1<\/span> Laravel and Other Modern PHP Frameworks<\/a><\/li><li><a href=\"#WordPress_wp-configphp_SALTs\"><span class=\"toc_number toc_depth_2\">5.2<\/span> WordPress (wp-config.php + SALTs)<\/a><\/li><li><a href=\"#Nodejs_Applications\"><span class=\"toc_number toc_depth_2\">5.3<\/span> Node.js Applications<\/a><\/li><\/ul><\/li><li><a href=\"#Git_CICD_and_Secrets_What_Belongs_in_the_Repository\"><span class=\"toc_number toc_depth_1\">6<\/span> Git, CI\/CD and Secrets: What Belongs in the Repository?<\/a><ul><li><a href=\"#Using_envexample_Correctly\"><span class=\"toc_number toc_depth_2\">6.1<\/span> Using .env.example Correctly<\/a><\/li><li><a href=\"#Handling_Secrets_in_CICD_Pipelines\"><span class=\"toc_number toc_depth_2\">6.2<\/span> Handling Secrets in CI\/CD Pipelines<\/a><\/li><\/ul><\/li><li><a href=\"#Advanced_Secret_Management_on_a_VPS_Encryption_and_Rotation\"><span class=\"toc_number toc_depth_1\">7<\/span> Advanced Secret Management on a VPS: Encryption and Rotation<\/a><ul><li><a href=\"#Encrypted_env_Files_with_sops_age\"><span class=\"toc_number toc_depth_2\">7.1<\/span> Encrypted .env Files with sops + age<\/a><\/li><li><a href=\"#Secret_Rotation_Policy\"><span class=\"toc_number toc_depth_2\">7.2<\/span> Secret Rotation Policy<\/a><\/li><\/ul><\/li><li><a href=\"#Backups_Snapshots_and_env_Files\"><span class=\"toc_number toc_depth_1\">8<\/span> Backups, Snapshots and .env Files<\/a><ul><li><a href=\"#What_to_Include\"><span class=\"toc_number toc_depth_2\">8.1<\/span> What to Include<\/a><\/li><li><a href=\"#Masking_Secrets_in_Logs_and_Monitoring\"><span class=\"toc_number toc_depth_2\">8.2<\/span> Masking Secrets in Logs and Monitoring<\/a><\/li><\/ul><\/li><li><a href=\"#When_to_Move_Beyond_env_Dedicated_Secret_Managers\"><span class=\"toc_number toc_depth_1\">9<\/span> When to Move Beyond .env: Dedicated Secret Managers<\/a><\/li><li><a href=\"#Putting_It_All_Together_on_a_dchostcom_VPS\"><span class=\"toc_number toc_depth_1\">10<\/span> Putting It All Together on a dchost.com VPS<\/a><\/li><\/ul><\/div>\n<h2><span id=\"What_env_Files_and_Secrets_Really_Are_and_Why_They_Matter_on_a_VPS\">What .env Files and Secrets Really Are (and Why They Matter on a VPS)<\/span><\/h2>\n<p>In most frameworks, a <strong>.env file<\/strong> is a simple text file containing key\u2013value pairs, for example:<\/p>\n<pre>APP_ENV=production\nAPP_DEBUG=false\nDB_HOST=127.0.0.1\nDB_DATABASE=myapp\nDB_USERNAME=myapp_user\nDB_PASSWORD=&lt;very-secret&gt;\nMAIL_HOST=smtp.example.com\nMAIL_PASSWORD=&lt;another-secret&gt;<\/pre>\n<p>These values are the <strong>secrets<\/strong> that unlock your infrastructure:<\/p>\n<ul>\n<li>Database usernames and passwords<\/li>\n<li>Redis and queue passwords<\/li>\n<li>API tokens for payment gateways, SMS, shipping and maps<\/li>\n<li>OAuth client IDs\/secrets for sign-in providers<\/li>\n<li>Encryption keys, JWT signing keys, webhook secrets<\/li>\n<\/ul>\n<p>Whether you store them in a .env file or as process environment variables, anyone who can read these values can often:<\/p>\n<ul>\n<li>Dump your entire customer database<\/li>\n<li>Send email or SMS in your name<\/li>\n<li>Issue refunds or charge cards via payment APIs<\/li>\n<li>Access internal admin panels or private APIs<\/li>\n<\/ul>\n<p>On a VPS you control the whole operating system, which is powerful but also risky: a single misconfigured folder or backup job can expose everything. That is why secrets must be treated at least as carefully as your source code, and in many cases even more carefully.<\/p>\n<h2><span id=\"Common_env_Mistakes_We_Keep_Seeing_on_VPS_Servers\">Common .env Mistakes We Keep Seeing on VPS Servers<\/span><\/h2>\n<p>Before we talk about good practices, it helps to recognize the patterns that cause trouble again and again. In our VPS security reviews and setups at dchost.com, we see a few recurring issues.<\/p>\n<h3><span id=\"1_env_Files_Inside_the_Public_Web_Root\">1. .env Files Inside the Public Web Root<\/span><\/h3>\n<p>Example: Laravel installed under <code>\/var\/www\/html<\/code> and the <code>.env<\/code> file sits right next to <code>index.php<\/code>, with a web server rule that does <em>not<\/em> block access to dotfiles. A misconfiguration, an added alias, or an Nginx rewrite mistake later, and <code>https:\/\/example.com\/.env<\/code> suddenly returns your entire secret set as plain text.<\/p>\n<p>Even if your current configuration blocks dotfiles, putting .env under the public root increases the blast radius if someone changes vhost rules, adds a static file alias, or migrates to a different server setup.<\/p>\n<h3><span id=\"2_env_Committed_to_Git_Repositories\">2. .env Committed to Git Repositories<\/span><\/h3>\n<p>Another classic: <code>.env<\/code> not in <code>.gitignore<\/code>, pushed to a remote repository along with application code. Even in a private repo, this is dangerous:<\/p>\n<ul>\n<li>Multiple developers and services gain access to live production secrets<\/li>\n<li>Old branches and forks preserve secrets long after they should be rotated<\/li>\n<li>Leaked Git backups or misconfigured Git HTTP access expose everything<\/li>\n<\/ul>\n<p>Once a secret hits Git history, you must assume it is compromised and rotate it.<\/p>\n<h3><span id=\"3_Overly_Permissive_File_Permissions\">3. Overly Permissive File Permissions<\/span><\/h3>\n<p>We frequently see .env files with permissions like <code>644<\/code> or even <code>664<\/code>, owned by <code>root<\/code> or a generic user that many system processes can impersonate. That means any local user account, misbehaving script, PHP process under a different pool, or compromised site on the same server can read your secrets.<\/p>\n<p>.env files should typically be readable only by the user account that runs the application process (or by a dedicated configuration user) \u2014 no one else.<\/p>\n<h3><span id=\"4_Secrets_Leaking_into_Logs_and_Debug_Pages\">4. Secrets Leaking into Logs and Debug Pages<\/span><\/h3>\n<p>Debugging output that dumps entire configuration arrays, or error logs that print full exception traces including connection strings, are a silent but very real leak channel. If you have not yet tuned your PHP and web server logging, it is worth reading our guide on <a href=\"https:\/\/www.dchost.com\/blog\/en\/php-hata-kayitlarini-dogru-yapilandirmak-display_errors-error_log-ve-log_level\/\">PHP error logging best practices on hosting servers<\/a> to avoid accidentally recording sensitive configuration values.<\/p>\n<h3><span id=\"5_Backups_and_Snapshots_Without_a_Secret_Strategy\">5. Backups and Snapshots Without a Secret Strategy<\/span><\/h3>\n<p>Backups that include .env files are necessary, but if those backups are copied to unencrypted storage, e-mailed as archives, or synced to a shared location without access control, your secrets travel much further than intended. We will come back to backup strategy later, because .env security does not stop at the VPS filesystem.<\/p>\n<h2><span id=\"Designing_a_Secure_env_Layout_on_a_VPS\">Designing a Secure .env Layout on a VPS<\/span><\/h2>\n<p>Let\u2019s walk through a clean, repeatable layout you can use on almost any Linux VPS for a typical web application.<\/p>\n<h3><span id=\"1_Separate_Code_from_Configuration\">1. Separate Code from Configuration<\/span><\/h3>\n<p>As a general rule:<\/p>\n<ul>\n<li><strong>Code<\/strong> (Git repository) goes under something like <code>\/var\/www\/myapp\/releases\/...<\/code><\/li>\n<li><strong>Configuration<\/strong> (including .env) goes under something like <code>\/etc\/myapp\/<\/code> or <code>\/var\/www\/myapp\/shared\/<\/code><\/li>\n<\/ul>\n<p>Your deployment process then symlinks or points the runtime to the right configuration location. This matches the 12-factor app principle: build once, configure per environment.<\/p>\n<h3><span id=\"2_Place_env_Outside_the_Public_Web_Root\">2. Place .env Outside the Public Web Root<\/span><\/h3>\n<p>For a PHP or Node.js app served via Nginx\/Apache, your public root is usually something like:<\/p>\n<ul>\n<li><code>\/var\/www\/myapp\/current\/public<\/code> (Laravel)<\/li>\n<li><code>\/var\/www\/myapp\/current\/public_html<\/code> (classic PHP)<\/li>\n<li><code>\/var\/www\/myapp\/current\/dist<\/code> (SPA\/static bundle)<\/li>\n<\/ul>\n<p>A good pattern is:<\/p>\n<ul>\n<li>Public web root: <code>\/var\/www\/myapp\/current\/public<\/code><\/li>\n<li>Application code: <code>\/var\/www\/myapp\/current<\/code><\/li>\n<li>Shared config: <code>\/var\/www\/myapp\/shared\/.env<\/code> (or <code>\/etc\/myapp\/myapp.env<\/code>)<\/li>\n<\/ul>\n<p>The application process or framework is configured to read the .env from the shared path, never from under the public root.<\/p>\n<h3><span id=\"3_Use_a_Dedicated_Unix_User\">3. Use a Dedicated Unix User<\/span><\/h3>\n<p>On a VPS it is tempting to run everything as <code>root<\/code> or <code>www-data<\/code>. A safer approach:<\/p>\n<ul>\n<li>Create a dedicated user for each major application, e.g. <code>myapp<\/code><\/li>\n<li>Run PHP-FPM pool, Node.js process, or queue workers as <code>myapp<\/code><\/li>\n<li>Make <code>myapp<\/code> the owner of the .env file<\/li>\n<\/ul>\n<p>This way, even if another site on the same VPS is compromised, it is harder for an attacker to read your <code>myapp<\/code> configuration files.<\/p>\n<h3><span id=\"4_Lock_Down_File_Permissions_Correctly\">4. Lock Down File Permissions Correctly<\/span><\/h3>\n<p>For a single-tenant app, a good baseline is:<\/p>\n<pre>sudo chown myapp:myapp \/var\/www\/myapp\/shared\/.env\nsudo chmod 600 \/var\/www\/myapp\/shared\/.env<\/pre>\n<p><code>600<\/code> means: readable and writable only by owner; no group or other permissions. If you must let the web server user read the file (e.g. <code>www-data<\/code>), you can set:<\/p>\n<pre>sudo chown myapp:www-data \/var\/www\/myapp\/shared\/.env\nsudo chmod 640 \/var\/www\/myapp\/shared\/.env<\/pre>\n<p>Then configure PHP-FPM\/Node to run under the <code>myapp<\/code> user, and Nginx\/Apache workers that never need to read the .env keep running as <code>www-data<\/code>.<\/p>\n<h3><span id=\"5_Decide_Environment_File_vs_Native_Environment_Variables\">5. Decide: Environment File vs Native Environment Variables<\/span><\/h3>\n<p>There are two common patterns:<\/p>\n<ul>\n<li><strong>Environment file<\/strong>: a .env file loaded by a library like <code>vlucas\/phpdotenv<\/code> (Laravel), or the <code>dotenv<\/code> package in Node.js<\/li>\n<li><strong>Native environment variables<\/strong>: systemd <code>Environment=<\/code> or <code>EnvironmentFile=<\/code> entries that you read directly via <code>getenv()<\/code> \/ <code>$_ENV<\/code> \/ <code>process.env<\/code><\/li>\n<\/ul>\n<p>Both are valid. For most existing projects, keeping a .env file and having systemd or a dotenv library load it is the least disruptive path. In more advanced setups, you might prefer to store secrets in a secure store and inject them directly as environment variables via systemd, without a .env file on disk at all.<\/p>\n<h2><span id=\"Step-by-Step_Locking_Down_env_on_an_UbuntuDebian_VPS\">Step-by-Step: Locking Down .env on an Ubuntu\/Debian VPS<\/span><\/h2>\n<p>Let\u2019s outline a concrete flow for a typical app deployed to an Ubuntu or Debian VPS. Adjust paths and usernames as needed.<\/p>\n<h3><span id=\"1_Create_an_Application_User\">1. Create an Application User<\/span><\/h3>\n<pre>sudo adduser --system --group --home \/var\/www\/myapp myapp<\/pre>\n<p>This creates a system user and group called <code>myapp<\/code> with home directory <code>\/var\/www\/myapp<\/code>.<\/p>\n<h3><span id=\"2_Create_a_Shared_Configuration_Directory\">2. Create a Shared Configuration Directory<\/span><\/h3>\n<pre>sudo mkdir -p \/var\/www\/myapp\/shared\nsudo chown myapp:myapp \/var\/www\/myapp\/shared\nsudo chmod 750 \/var\/www\/myapp\/shared<\/pre>\n<p>The shared directory will hold <code>.env<\/code> and possibly other non-public configuration files.<\/p>\n<h3><span id=\"3_Create_the_env_File_Safely\">3. Create the .env File Safely<\/span><\/h3>\n<p>You can either create the .env file locally and upload via SFTP as the <code>myapp<\/code> user, or create it directly on the VPS:<\/p>\n<pre>sudo -u myapp nano \/var\/www\/myapp\/shared\/.env<\/pre>\n<p>Paste your key\u2013value pairs, save and exit. Then lock permissions:<\/p>\n<pre>sudo chown myapp:myapp \/var\/www\/myapp\/shared\/.env\nsudo chmod 600 \/var\/www\/myapp\/shared\/.env<\/pre>\n<h3><span id=\"4_Configure_Your_Application_to_Use_This_Path\">4. Configure Your Application to Use This Path<\/span><\/h3>\n<p>For Laravel, you can set the <code>APP_ENV_FILE<\/code> path via bootstrap or use a small wrapper to pass the correct file to <code>phpdotenv<\/code>. For Node.js with <code>dotenv<\/code>:<\/p>\n<pre>require('dotenv').config({\n  path: '\/var\/www\/myapp\/shared\/.env'\n});<\/pre>\n<p>For custom PHP apps, you can write a tiny loader at the start of <code>config.php<\/code> that parses <code>\/var\/www\/myapp\/shared\/.env<\/code> and populates <code>$_ENV<\/code> or constants.<\/p>\n<h3><span id=\"5_Use_systemd_to_Run_the_App_Under_the_Right_User\">5. Use systemd to Run the App Under the Right User<\/span><\/h3>\n<p>If you manage your app with systemd (recommended on a VPS), a sample service unit might look like:<\/p>\n<pre>[Unit]\nDescription=MyApp PHP-FPM Worker\nAfter=network.target\n\n[Service]\nUser=myapp\nGroup=myapp\nWorkingDirectory=\/var\/www\/myapp\/current\nEnvironmentFile=\/var\/www\/myapp\/shared\/.env\nExecStart=\/usr\/bin\/php artisan queue:work --sleep=3 --tries=3\nRestart=always\nRestartSec=5\n\n[Install]\nWantedBy=multi-user.target<\/pre>\n<p>Here, systemd loads environment variables from your .env file before launching the process. This works well for queue workers, schedulers and custom daemons. For PHP-FPM or Nginx themselves, you typically reference the .env inside the app bootstrap or framework instead.<\/p>\n<p>If you are new to systemd and process management on a VPS, it is worth pairing this with our <a href=\"https:\/\/www.dchost.com\/blog\/en\/vps-guvenlik-sertlestirme-kontrol-listesi-sshd_config-fail2ban-ve-root-erisimini-kapatmak\/\">VPS security hardening checklist<\/a>, which also covers safe service configuration and user isolation.<\/p>\n<h2><span id=\"Framework-Specific_Notes_Laravel_WordPress_and_Nodejs\">Framework-Specific Notes: Laravel, WordPress and Node.js<\/span><\/h2>\n<p>Different stacks have slightly different expectations around .env handling. Let\u2019s look at a few common ones.<\/p>\n<h3><span id=\"Laravel_and_Other_Modern_PHP_Frameworks\">Laravel and Other Modern PHP Frameworks<\/span><\/h3>\n<p>Laravel (and many similar frameworks) uses <code>.env<\/code> heavily. Key points:<\/p>\n<ul>\n<li>Make sure <code>.env<\/code> is <strong>not<\/strong> inside the document root (<code>public<\/code> directory).<\/li>\n<li>Always add <code>\/.env<\/code> to <code>.gitignore<\/code>.<\/li>\n<li>Commit a <code>.env.example<\/code> file to show required keys without real values.<\/li>\n<li>In production, consider using <code>php artisan config:cache<\/code> so your app does not read .env on every request.<\/li>\n<\/ul>\n<p>If you are tuning PHP-FPM and OPcache for performance at the same time, you can combine this work with the recommendations in our guide on <a href=\"https:\/\/www.dchost.com\/blog\/en\/php-opcache-ayarlari-wordpress-laravel-ve-woocommerce-icin-en-iyi-konfigurasyon-rehberi\/\">PHP OPcache settings for WordPress, Laravel and WooCommerce<\/a>.<\/p>\n<h3><span id=\"WordPress_wp-configphp_SALTs\">WordPress (wp-config.php + SALTs)<\/span><\/h3>\n<p>WordPress does not use a .env file by default, but the same secret management issues apply. Database credentials, AUTH\/SECURE salts and keys, and SMTP settings often live in <code>wp-config.php<\/code>. Better patterns include:<\/p>\n<ul>\n<li>Move <code>wp-config.php<\/code> one level above the web root if possible.<\/li>\n<li>Load sensitive values from environment variables set by systemd or an external file.<\/li>\n<li>Keep file permissions tight (e.g. <code>640<\/code> owned by a dedicated user).<\/li>\n<\/ul>\n<p>For a broader look at tightening WordPress, see our <a href=\"https:\/\/www.dchost.com\/blog\/en\/paylasimli-hostingde-wordpress-guvenligi-eklentiler-waf-2fa-ve-yedekler\/\">WordPress security checklist on shared hosting<\/a> \u2014 the principles carry over directly to VPS environments as well.<\/p>\n<h3><span id=\"Nodejs_Applications\">Node.js Applications<\/span><\/h3>\n<p>Most Node apps use the <code>dotenv<\/code> package in development, and often production as well. Best practices:<\/p>\n<ul>\n<li>Use an explicit path: <code>dotenv.config({ path: '\/var\/www\/myapp\/shared\/.env' })<\/code><\/li>\n<li>Never commit <code>.env<\/code>; provide <code>.env.example<\/code> instead.<\/li>\n<li>For containers, prefer native environment variables injected at runtime.<\/li>\n<\/ul>\n<p>When you run Node.js in production on a VPS under systemd, you can either let systemd load the .env via <code>EnvironmentFile<\/code> or read it from the filesystem inside your Node code. Both are valid; pick one approach and standardize across your projects.<\/p>\n<h2><span id=\"Git_CICD_and_Secrets_What_Belongs_in_the_Repository\">Git, CI\/CD and Secrets: What Belongs in the Repository?<\/span><\/h2>\n<p>A lot of .env trouble starts from confusing what lives in Git vs what lives only on servers. A simple rule of thumb:<\/p>\n<ul>\n<li><strong>In Git:<\/strong> <code>.env.example<\/code>, <code>.env.testing<\/code>, maybe <code>.env.local<\/code> for a fully local stack without real third-party accounts.<\/li>\n<li><strong>Never in Git:<\/strong> live production database passwords, real payment API keys, live SMTP credentials, long-term encryption keys.<\/li>\n<\/ul>\n<h3><span id=\"Using_envexample_Correctly\">Using .env.example Correctly<\/span><\/h3>\n<p>Your <code>.env.example<\/code> file should:<\/p>\n<ul>\n<li>List <em>all<\/em> required keys<\/li>\n<li>Contain placeholder or obviously fake values<\/li>\n<li>Be safe to publish publicly (assume it can leak)<\/li>\n<\/ul>\n<p>This file becomes documentation for future team members and for CI\/CD pipelines that need to know which variables to inject.<\/p>\n<h3><span id=\"Handling_Secrets_in_CICD_Pipelines\">Handling Secrets in CI\/CD Pipelines<\/span><\/h3>\n<p>Modern CI\/CD tools often include a secure secrets store. The usual pattern is:<\/p>\n<ol>\n<li>Store production and staging secrets in the CI\/CD system\u2019s encrypted variables.<\/li>\n<li>On deploy, the pipeline connects to your VPS and writes or updates the .env file from those variables.<\/li>\n<li>The pipeline reloads or restarts the application via systemd with zero or minimal downtime.<\/li>\n<\/ol>\n<p>We show this style of deployment \u2014 including how to use <code>rsync<\/code>, symlinked release directories, and systemd restarts without downtime \u2014 in our guide on <a href=\"https:\/\/www.dchost.com\/blog\/en\/vpse-sifir-kesinti-ci-cd-nasil-kurulur-rsync-sembolik-surumler-ve-systemd-ile-sicacik-bir-yolculuk\/\">zero\u2011downtime CI\/CD to a VPS<\/a>. Combine that deployment pattern with the secret-handling rules here and you get a very robust pipeline.<\/p>\n<p>Key CI\/CD rules:<\/p>\n<ul>\n<li>Never print secrets in pipeline logs (no <code>echo $DB_PASSWORD<\/code>).<\/li>\n<li>Restrict who can see pipeline variables.<\/li>\n<li>Rotate CI\/CD credentials regularly and revoke unused tokens.<\/li>\n<\/ul>\n<h2><span id=\"Advanced_Secret_Management_on_a_VPS_Encryption_and_Rotation\">Advanced Secret Management on a VPS: Encryption and Rotation<\/span><\/h2>\n<p>Plaintext .env files are simple and work well if you control access to the VPS and keep backups secure. As projects and teams grow, you may want stronger guarantees: encrypted-at-rest configuration, auditable updates and painless rotation.<\/p>\n<h3><span id=\"Encrypted_env_Files_with_sops_age\">Encrypted .env Files with sops + age<\/span><\/h3>\n<p>One practical pattern is to store an <em>encrypted<\/em> version of your .env in Git, and decrypt it only on the VPS at deploy time. A typical stack uses:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/getsops\/sops\" rel=\"nofollow noopener\" target=\"_blank\">sops<\/a> for structured secret management<\/li>\n<li><a href=\"https:\/\/github.com\/FiloSottile\/age\" rel=\"nofollow noopener\" target=\"_blank\">age<\/a> for modern encryption keys<\/li>\n<\/ul>\n<p>You commit <code>.env.sops.yaml<\/code> (encrypted) to Git, not the plaintext .env. Your deploy script, running on the VPS, decrypts this file into <code>\/var\/www\/myapp\/shared\/.env<\/code> using a deployment key stored only on that server.<\/p>\n<p>We have an entire, hands-on playbook about this approach in our article <a href=\"https:\/\/www.dchost.com\/blog\/en\/vpste-secrets-yonetimi-nasil-tatli-tatli-cozulur-sops-age-gitops-akisi-systemd-ve-rotasyon\/\">The Calm Way to Secrets on a VPS: GitOps with sops + age, systemd and rotation<\/a>. If you want Git-based configuration and reproducible deployments without exposing secrets, that article is a natural next step.<\/p>\n<h3><span id=\"Secret_Rotation_Policy\">Secret Rotation Policy<\/span><\/h3>\n<p>Regardless of tooling, you need a plan for <strong>rotation<\/strong> \u2014 changing secrets on a schedule or after incidents:<\/p>\n<ul>\n<li>Database passwords rotated every few months (or faster if there is any suspicion of leakage).<\/li>\n<li>API keys regenerated when staff change, or when third-party providers indicate a risk.<\/li>\n<li>JWT and encryption keys approached carefully: rotation strategies must consider active sessions and data format.<\/li>\n<\/ul>\n<p>On a VPS, rotation is typically implemented as a small script plus a scheduled job. Our guide on <a href=\"https:\/\/www.dchost.com\/blog\/en\/linux-crontab-en-iyi-uygulamalar-rehberi-yedek-rapor-ve-bakim-isleri-icin-guvenli-zamanlama\/\">Linux crontab best practices<\/a> walks through how to schedule tasks safely, including logging and failure alerts.<\/p>\n<h2><span id=\"Backups_Snapshots_and_env_Files\">Backups, Snapshots and .env Files<\/span><\/h2>\n<p>Secrets are not safe if they only live in one place. But including them in backups without planning can increase your risk. On a VPS, you usually have three layers:<\/p>\n<ul>\n<li>Application-level backups (database dumps, file archives)<\/li>\n<li>Server-level backups (filesystem snapshots, rsync backups)<\/li>\n<li>Off-site backups (object storage, another data center)<\/li>\n<\/ul>\n<h3><span id=\"What_to_Include\">What to Include<\/span><\/h3>\n<p>In most cases, you <strong>should<\/strong> back up your .env files because they contain operational knowledge that can be hard to reconstruct under stress. However:<\/p>\n<ul>\n<li>Ensure your backup buckets or remote servers are protected by strong authentication.<\/li>\n<li>Encrypt backups at rest (e.g. using restic or Borg with strong passphrases).<\/li>\n<li>Limit access to backup storage accounts as tightly as production.<\/li>\n<\/ul>\n<p>If you are designing a full backup strategy for your stack, we recommend pairing this with our guide to the <a href=\"https:\/\/www.dchost.com\/blog\/en\/3-2-1-yedekleme-stratejisi-neden-ise-yariyor-cpanel-plesk-ve-vpste-otomatik-yedekleri-nasil-kurarsin\/\">3\u20112\u20111 backup strategy and automating backups on VPS servers<\/a>, which also covers off-site storage patterns.<\/p>\n<h3><span id=\"Masking_Secrets_in_Logs_and_Monitoring\">Masking Secrets in Logs and Monitoring<\/span><\/h3>\n<p>Many modern frameworks and loggers support secret masking, replacing values that look like API keys or passwords with <code>***<\/code> in logs. Enable these features where available. Reviewing logs and metrics from your dchost.com VPS should help you diagnose issues \u2014 not leak credentials.<\/p>\n<h2><span id=\"When_to_Move_Beyond_env_Dedicated_Secret_Managers\">When to Move Beyond .env: Dedicated Secret Managers<\/span><\/h2>\n<p>For many small and medium projects, well-managed .env files on a properly secured VPS are enough. But there are cases where you might want a dedicated secret management system:<\/p>\n<ul>\n<li>Multiple services and microservices sharing secrets<\/li>\n<li>Strict compliance environments (PCI DSS, banking, healthcare)<\/li>\n<li>Large teams with fine-grained access requirements and audit needs<\/li>\n<\/ul>\n<p>Self-hosted tools like HashiCorp Vault or open-source alternatives can run on a dedicated dchost.com VPS or <a href=\"https:\/\/www.dchost.com\/dedicated-server\">dedicated server<\/a>. In that model, your applications obtain short-lived secrets via an authenticated API call at startup, and long-term master keys live only in the secret manager. This is more complex to set up and operate, but it can significantly reduce the impact of a single compromised host.<\/p>\n<h2><span id=\"Putting_It_All_Together_on_a_dchostcom_VPS\">Putting It All Together on a dchost.com VPS<\/span><\/h2>\n<p>Managing .env files and secrets securely on a VPS is not about fancy tools first; it starts with a few disciplined habits:<\/p>\n<ul>\n<li>Keep .env out of the public web root and out of Git history.<\/li>\n<li>Use dedicated Unix users and correct permissions (600\/640) for configuration files.<\/li>\n<li>Use systemd to run your services under the right user with explicit EnvironmentFile entries where appropriate.<\/li>\n<li>Plan CI\/CD flows so that secrets are injected at deploy time from secure variables, not hard-coded in scripts.<\/li>\n<li>Back up .env files as part of a secure, encrypted 3\u20112\u20111 backup strategy, with regular rotation of sensitive keys.<\/li>\n<\/ul>\n<p>From our side at dchost.com, we see best results when customers treat secrets as part of their overall VPS architecture, not an afterthought. When you combine the patterns in this guide with a hardened base server (SSH security, firewalls, Fail2ban, timely updates) and good monitoring, your risk profile drops dramatically. Our articles on <a href=\"https:\/\/www.dchost.com\/blog\/en\/vps-guvenlik-sertlestirme-kontrol-listesi-sshd_config-fail2ban-ve-root-erisimini-kapatmak\/\">VPS security hardening<\/a> and <a href=\"https:\/\/www.dchost.com\/blog\/en\/vps-izleme-ve-uyari-nasil-kurulur-prometheus-grafana-ve-node-exporter-ile-sessiz-alarmlari-konusturmak\/\">VPS monitoring and alerts<\/a> are natural companions to this one.<\/p>\n<p>If you are planning a new project or want to review an existing one, our VPS, dedicated server and colocation options give you the control you need to implement all these practices cleanly. Start with a VPS plan that fits your CPU, RAM and storage needs, structure your deployments and .env management as described, and you will have a foundation that scales from prototype to production without having to re-think how you handle secrets later.<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>API keys, database passwords, SMTP credentials, license keys, third-party webhooks\u2026 almost every modern application running on a VPS depends on sensitive configuration values. Most teams put these values into .env files or environment variables, but the way those secrets are stored on the server often gets far less attention than the application code itself. As [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":3945,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[26],"tags":[],"class_list":["post-3944","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-teknoloji"],"_links":{"self":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/posts\/3944","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/comments?post=3944"}],"version-history":[{"count":0,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/posts\/3944\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/media\/3945"}],"wp:attachment":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/media?parent=3944"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/categories?post=3944"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/tags?post=3944"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}