<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yousuf Basir</title>
    <description>The latest articles on DEV Community by Yousuf Basir (@yousufbasir).</description>
    <link>https://dev.to/yousufbasir</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F310938%2F5fd4b3e7-e200-4e10-88f8-b531d9de3aa4.jpeg</url>
      <title>DEV Community: Yousuf Basir</title>
      <link>https://dev.to/yousufbasir</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yousufbasir"/>
    <language>en</language>
    <item>
      <title>Secure Linux Server Setup &amp; Application Deployment</title>
      <dc:creator>Yousuf Basir</dc:creator>
      <pubDate>Thu, 18 Dec 2025 14:00:47 +0000</pubDate>
      <link>https://dev.to/yousufbasir/secure-linux-server-setup-application-deployment-i91</link>
      <guid>https://dev.to/yousufbasir/secure-linux-server-setup-application-deployment-i91</guid>
      <description>&lt;p&gt;&lt;em&gt;A Practical, Opinionated Guide for Real Production Servers&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Deploying an application is easy.&lt;br&gt;
Deploying it &lt;strong&gt;securely&lt;/strong&gt;, so that one compromised app does not take down your entire server, requires discipline and structure.&lt;/p&gt;

&lt;p&gt;This guide documents the &lt;strong&gt;exact process we follow&lt;/strong&gt; to prepare a fresh Linux server, deploy databases and applications, and keep the system secure, isolated, and maintainable.&lt;/p&gt;

&lt;p&gt;This is not theory.&lt;br&gt;
This is a &lt;strong&gt;battle-tested setup&lt;/strong&gt; suitable for real production servers.&lt;/p&gt;


&lt;h2&gt;
  
  
  What This Guide Is For
&lt;/h2&gt;

&lt;p&gt;This setup works for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js backends (NestJS, Express, Fastify)&lt;/li&gt;
&lt;li&gt;Next.js (standalone build)&lt;/li&gt;
&lt;li&gt;Static frontends (React / Vite)&lt;/li&gt;
&lt;li&gt;Databases (PostgreSQL, MongoDB, Redis) using Docker&lt;/li&gt;
&lt;li&gt;Reverse proxy with HTTPS (Caddy / Nginx)&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Core Security Philosophy
&lt;/h2&gt;

&lt;p&gt;Before commands, understand the &lt;strong&gt;rules&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Root is not an app runtime&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;One app = one service user&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Humans deploy, services run&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Databases are private by default&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reverse proxy is the only public entry point&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Assume one app will eventually be compromised&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Our goal is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If one application is hacked, &lt;strong&gt;everything else must remain safe&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Step 1 — Create a Non-Root Admin User
&lt;/h2&gt;

&lt;p&gt;On a fresh server, you usually start as &lt;code&gt;root&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create a normal admin user (example: &lt;code&gt;dev&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adduser dev
usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Root SSH access is dangerous&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;sudo&lt;/code&gt; is auditable&lt;/li&gt;
&lt;li&gt;Accidents are easier to recover from&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 2 — Harden SSH Access
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Generate SSH key (on your local machine)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the public key to the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /home/dev/.ssh
nano /home/dev/.ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fix permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; dev:dev /home/dev/.ssh
&lt;span class="nb"&gt;chmod &lt;/span&gt;700 /home/dev/.ssh
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 /home/dev/.ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Disable dangerous SSH options
&lt;/h3&gt;

&lt;p&gt;Edit SSH config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/ssh/sshd_config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PermitRootLogin no
PubkeyAuthentication yes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Disable password login:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Match all
    PasswordAuthentication no
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reload SSH safely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3 — Enable Firewall (UFW)
&lt;/h2&gt;

&lt;p&gt;Allow only what’s required:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow OpenSSH
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 80
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 443
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw &lt;span class="nb"&gt;enable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw status verbose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Only ports 22, 80, 443 should be public.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4 — Install Docker (Admin User Only)
&lt;/h2&gt;

&lt;p&gt;Docker is treated as &lt;strong&gt;root-equivalent&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only the admin user (&lt;code&gt;dev&lt;/code&gt;) may use Docker&lt;/li&gt;
&lt;li&gt;Application users never touch Docker&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install Docker from the &lt;strong&gt;official repository&lt;/strong&gt; (not &lt;code&gt;apt docker.io&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;After installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Re-login and verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🚫 Never add service users to the &lt;code&gt;docker&lt;/code&gt; group.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5 — Run Databases Securely in Docker
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Key rules for databases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Never expose DB ports publicly&lt;/li&gt;
&lt;li&gt;Bind to &lt;code&gt;127.0.0.1&lt;/code&gt; only&lt;/li&gt;
&lt;li&gt;Use Docker volumes&lt;/li&gt;
&lt;li&gt;Access from local machine via &lt;strong&gt;SSH tunneling&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example: PostgreSQL (secure)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; postgres &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; unless-stopped &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;appuser &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;STRONG_PASSWORD &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;appdb &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; pgdata:/var/lib/postgresql &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 127.0.0.1:5432:5432 &lt;span class="se"&gt;\&lt;/span&gt;
  postgres:18
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ss &lt;span class="nt"&gt;-tulpn&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;5432
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1:5432
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important note on port binding:&lt;/strong&gt;&lt;br&gt;
We explicitly bind database ports to &lt;code&gt;127.0.0.1&lt;/code&gt; because Docker &lt;strong&gt;does not honor UFW rules&lt;/strong&gt; for published ports. Docker inserts its own iptables rules, so if a container port is mapped to &lt;code&gt;0.0.0.0&lt;/code&gt;, it will be publicly accessible &lt;strong&gt;even if UFW blocks that port&lt;/strong&gt;. Binding to &lt;code&gt;127.0.0.1&lt;/code&gt; ensures the database is reachable only from the server itself and never exposed to the internet.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 6 — Access Databases via SSH Tunnel
&lt;/h2&gt;

&lt;p&gt;From your local machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; 5432:127.0.0.1:5432 dev@SERVER_IP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now connect locally using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Host: &lt;code&gt;127.0.0.1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Port: &lt;code&gt;5432&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔐 Encrypted, private, safe.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7 — One GitHub Deploy Key per Repository
&lt;/h2&gt;

&lt;p&gt;For each private repository, we generate a &lt;strong&gt;dedicated SSH deploy key&lt;/strong&gt; on the server (as the admin user):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"deploy-myapp"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use a unique filename per repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.ssh/id_ed25519_myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the &lt;strong&gt;public key&lt;/strong&gt; to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GitHub → Repository → Settings → Deploy keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Grant &lt;strong&gt;read-only access&lt;/strong&gt; and clone the repository using an SSH alias (not HTTPS).&lt;/p&gt;

&lt;p&gt;👉 For the full, step-by-step explanation (SSH config, aliases, and examples), see:&lt;br&gt;
&lt;a href="https://dev.to/yousufbasir/securely-managing-github-access-on-production-servers-20l3"&gt;https://dev.to/yousufbasir/securely-managing-github-access-on-production-servers-20l3&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 8 — Create a Service User (Per App)
&lt;/h2&gt;

&lt;p&gt;Each app runs as its &lt;strong&gt;own locked-down user&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;adduser &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--no-create-home&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--group&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--shell&lt;/span&gt; /usr/sbin/nologin &lt;span class="se"&gt;\&lt;/span&gt;
  svc-myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This user:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cannot SSH&lt;/li&gt;
&lt;li&gt;has no shell&lt;/li&gt;
&lt;li&gt;has no sudo&lt;/li&gt;
&lt;li&gt;owns only its app directory&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 9 — Clone &amp;amp; Build as Admin User
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /var/apps
git clone git@github.com-myapp:org/repo.git
&lt;span class="nb"&gt;cd &lt;/span&gt;repo

npm ci
npm run build
npm prune &lt;span class="nt"&gt;--production&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Humans build.&lt;br&gt;
Services only run.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 10 — Environment Variables (Build-time vs Runtime)
&lt;/h2&gt;

&lt;p&gt;In production, &lt;strong&gt;environment variables are never committed to Git&lt;/strong&gt;.&lt;br&gt;
They are managed by the system and injected &lt;strong&gt;only when required&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We store runtime environment variables in a system-managed file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/etc/systemd/system/myapp.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file is owned by &lt;code&gt;root&lt;/code&gt; and loaded by systemd at runtime.&lt;/p&gt;

&lt;h3&gt;
  
  
  Special case: Next.js public environment variables
&lt;/h3&gt;

&lt;p&gt;If a Next.js project uses variables starting with &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt;, those &lt;strong&gt;must be available at build time&lt;/strong&gt;, because the Next.js compiler embeds them into the generated JavaScript.&lt;/p&gt;

&lt;p&gt;In that case, we explicitly inject the env file when building:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'
  set -a
  source /etc/systemd/system/myapp.env
  set +a

  npm run build
'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If there are &lt;strong&gt;no &lt;code&gt;NEXT_PUBLIC_*&lt;/code&gt; variables&lt;/strong&gt;, this step is not required—runtime injection via systemd is enough.&lt;/p&gt;




&lt;h3&gt;
  
  
  Next.js standalone build (required asset copy)
&lt;/h3&gt;

&lt;p&gt;When building Next.js as a &lt;strong&gt;standalone application&lt;/strong&gt;, we must also copy static assets manually so the app can run without the full &lt;code&gt;.next&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;After &lt;code&gt;next build&lt;/code&gt;, we run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; .next/standalone/.next
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; .next/static .next/standalone/.next/
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; public .next/standalone/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why this is needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The standalone output contains only server code&lt;/li&gt;
&lt;li&gt;Static files (&lt;code&gt;_next/static&lt;/code&gt;, &lt;code&gt;public/&lt;/code&gt;) are not included automatically&lt;/li&gt;
&lt;li&gt;Without this step, the app will run but assets will 404&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This produces a fully self-contained Node.js app that can be started via systemd.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 11 — Transfer Ownership to Service User
&lt;/h2&gt;

&lt;p&gt;After build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; svc-myapp:svc-myapp /var/apps/myapp
&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; o-rwx /var/apps/myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, even &lt;code&gt;dev&lt;/code&gt; should get &lt;strong&gt;permission denied&lt;/strong&gt; — that’s intentional.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 12 — Run the App with systemd (Hardened)
&lt;/h2&gt;

&lt;p&gt;Example systemd service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;My Application&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network.target&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;svc-myapp&lt;/span&gt;
&lt;span class="py"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;svc-myapp&lt;/span&gt;
&lt;span class="py"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/var/apps/myapp&lt;/span&gt;
&lt;span class="py"&gt;EnvironmentFile&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/etc/systemd/system/myapp.env&lt;/span&gt;

&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/bin/node dist/main.js&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;span class="py"&gt;RestartSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;3&lt;/span&gt;

&lt;span class="c"&gt;# Security hardening
&lt;/span&gt;&lt;span class="py"&gt;NoNewPrivileges&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;ProtectSystem&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;strict&lt;/span&gt;
&lt;span class="py"&gt;ProtectHome&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;ReadWritePaths&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/var/apps/myapp&lt;/span&gt;
&lt;span class="py"&gt;PrivateTmp&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;ProtectKernelTunables&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;ProtectKernelModules&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;ProtectControlGroups&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;RestrictNamespaces&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;LockPersonality&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;RestrictSUIDSGID&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;CapabilityBoundingSet&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;
&lt;span class="py"&gt;AmbientCapabilities&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;
&lt;span class="py"&gt;UMask&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;0077&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable and start:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;myapp
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 13 — Static Frontend Builds (React / Vite)
&lt;/h2&gt;

&lt;p&gt;Static frontend apps (React, Vite, etc.) &lt;strong&gt;do not run via systemd&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;They are built once and served directly by the reverse proxy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Injecting environment variables at build time
&lt;/h3&gt;

&lt;p&gt;For static apps, public variables (e.g. &lt;code&gt;VITE_*&lt;/code&gt;) must be injected during the build.&lt;/p&gt;

&lt;p&gt;We place the env file in the project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.env.production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then build explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The build output (HTML, JS, CSS, assets) is now fully static and contains the injected values.&lt;/p&gt;




&lt;h3&gt;
  
  
  Granting Caddy read access
&lt;/h3&gt;

&lt;p&gt;Caddy runs as its own user and needs &lt;strong&gt;read-only access&lt;/strong&gt; to the static build folder.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; svc-frontend:svc-frontend /var/apps/frontend
&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 755 /var/apps/frontend/dist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why we do this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Caddy must read static files&lt;/li&gt;
&lt;li&gt;No write access is needed&lt;/li&gt;
&lt;li&gt;Prevents accidental or malicious file modification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Static apps have &lt;strong&gt;no runtime&lt;/strong&gt;, no open ports, and no background process—this significantly reduces attack surface.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 14 — Reverse Proxy Configuration (Caddy)
&lt;/h2&gt;

&lt;p&gt;Caddy is the &lt;strong&gt;only public entry point&lt;/strong&gt; to the server.&lt;br&gt;
All applications—dynamic or static—are exposed through it.&lt;/p&gt;


&lt;h3&gt;
  
  
  Example: Node.js app running via systemd
&lt;/h3&gt;

&lt;p&gt;The app runs internally on a private port (e.g. &lt;code&gt;127.0.0.1:5000&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Caddyfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;api.example.com {
    reverse_proxy 127.0.0.1:5000
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The app port is &lt;strong&gt;not exposed publicly&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Firewall blocks direct access&lt;/li&gt;
&lt;li&gt;Only Caddy can reach it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This applies to NestJS, Next.js (standalone), Express, etc.&lt;/p&gt;




&lt;h3&gt;
  
  
  Example: Static frontend site
&lt;/h3&gt;

&lt;p&gt;The static build lives at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/var/apps/frontend/dist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caddyfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.example.com {
    root * /var/apps/frontend/dist
    encode gzip zstd
    try_files {path} {path}/ /index.html
    file_server
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serves static files directly&lt;/li&gt;
&lt;li&gt;Supports client-side routing (SPA)&lt;/li&gt;
&lt;li&gt;Enables compression&lt;/li&gt;
&lt;li&gt;No Node.js process required&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Why this architecture matters
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Only ports 80 and 443 are public&lt;/li&gt;
&lt;li&gt;Apps never bind directly to the internet&lt;/li&gt;
&lt;li&gt;Static sites have zero runtime risk&lt;/li&gt;
&lt;li&gt;Dynamic apps are isolated behind systemd and firewall&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This clean separation keeps the server &lt;strong&gt;secure, observable, and easy to reason about&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 15 — Updating an App Safely
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; dev:dev /var/apps/myapp

&lt;span class="nb"&gt;cd&lt;/span&gt; /var/apps/myapp
git pull
npm ci
npm run build
npm prune &lt;span class="nt"&gt;--production&lt;/span&gt;

&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; svc-myapp:svc-myapp /var/apps/myapp
&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; o-rwx /var/apps/myapp

&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🚫 Never &lt;code&gt;sudo git pull&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Setup Protects Against
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Privilege escalation&lt;/li&gt;
&lt;li&gt;Lateral movement between apps&lt;/li&gt;
&lt;li&gt;Accidental data leaks&lt;/li&gt;
&lt;li&gt;Exposed databases&lt;/li&gt;
&lt;li&gt;Root-level compromise from app bugs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even if one app is hacked:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The system survives. Other apps survive. Data survives.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Security is not about tools.&lt;br&gt;
It’s about &lt;strong&gt;clear boundaries and boring defaults&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This setup avoids complexity, avoids magic, and relies on Linux doing what it already does best.&lt;/p&gt;

&lt;p&gt;That’s how small teams run production servers &lt;strong&gt;professionally&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;If you follow this guide end-to-end, your server will already be &lt;strong&gt;more secure than most production environments&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Happy (and secure) deploying 🚀&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>cloud</category>
      <category>node</category>
      <category>database</category>
    </item>
    <item>
      <title>Securely Managing GitHub Access on Production Servers</title>
      <dc:creator>Yousuf Basir</dc:creator>
      <pubDate>Thu, 18 Dec 2025 13:28:51 +0000</pubDate>
      <link>https://dev.to/yousufbasir/securely-managing-github-access-on-production-servers-20l3</link>
      <guid>https://dev.to/yousufbasir/securely-managing-github-access-on-production-servers-20l3</guid>
      <description>&lt;p&gt;This guide shows the &lt;strong&gt;correct, production-safe way&lt;/strong&gt; to let a server clone and update private GitHub repositories using &lt;strong&gt;deploy keys and SSH aliases&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This approach works for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;single servers&lt;/li&gt;
&lt;li&gt;multiple apps on one server&lt;/li&gt;
&lt;li&gt;teams with shared infrastructure&lt;/li&gt;
&lt;li&gt;long-lived production environments&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Common mistakes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using HTTPS with personal access tokens&lt;/li&gt;
&lt;li&gt;Using one SSH key for all repositories&lt;/li&gt;
&lt;li&gt;Logging in to GitHub from servers&lt;/li&gt;
&lt;li&gt;Giving write access when read-only is enough&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These mistakes increase blast radius.&lt;/p&gt;

&lt;p&gt;Our goal:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If a server or key is compromised, &lt;strong&gt;only one repository is affected&lt;/strong&gt; — nothing else.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Core Rules
&lt;/h2&gt;

&lt;p&gt;We follow these rules strictly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;One repository = one SSH key&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Servers never use personal GitHub accounts&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deploy keys are read-only&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSH aliases decide which key is used&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTPS is never used on production servers&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 1 — Generate a Deploy Key (Server Side)
&lt;/h2&gt;

&lt;p&gt;Log in to the server as the admin/deployment user (for example, &lt;code&gt;dev&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Generate a new SSH key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"deploy-myapp"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When prompted for the file name, &lt;strong&gt;do not use the default key&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Use a repository-specific name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/home/dev/.ssh/id_ed25519_myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Leave the passphrase empty.&lt;/p&gt;

&lt;p&gt;This creates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.ssh/id_ed25519_myapp
~/.ssh/id_ed25519_myapp.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each repository must have its &lt;strong&gt;own unique key&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2 — Configure an SSH Alias
&lt;/h2&gt;

&lt;p&gt;When multiple SSH keys exist, Git needs help choosing the right one.&lt;br&gt;
We solve this using &lt;strong&gt;SSH aliases&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Edit the SSH config file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano ~/.ssh/config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a new section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="err"&gt;Host&lt;/span&gt; &lt;span class="err"&gt;github.com-myapp&lt;/span&gt;
  &lt;span class="err"&gt;HostName&lt;/span&gt; &lt;span class="err"&gt;github.com&lt;/span&gt;
  &lt;span class="err"&gt;User&lt;/span&gt; &lt;span class="err"&gt;git&lt;/span&gt;
  &lt;span class="err"&gt;IdentityFile&lt;/span&gt; &lt;span class="err"&gt;~/.ssh/id_ed25519_myapp&lt;/span&gt;
  &lt;span class="err"&gt;IdentitiesOnly&lt;/span&gt; &lt;span class="err"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;github.com-myapp&lt;/code&gt; is an alias (not a real domain)&lt;/li&gt;
&lt;li&gt;It forces SSH to use &lt;code&gt;id_ed25519_myapp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Prevents Git from trying the wrong key&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is mandatory once you have more than one deploy key.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3 — Add the Deploy Key to GitHub
&lt;/h2&gt;

&lt;p&gt;On GitHub:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the repository&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Settings → Deploy keys&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add deploy key&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Paste the public key:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_ed25519_myapp.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Give it a clear name (e.g. &lt;code&gt;prod-server&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Enable &lt;strong&gt;read-only access&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Deploy keys are &lt;strong&gt;repository-scoped by design&lt;/strong&gt; — this is a security feature.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4 — Clone Using the SSH Alias (Not HTTPS)
&lt;/h2&gt;

&lt;p&gt;❌ Do &lt;strong&gt;not&lt;/strong&gt; use HTTPS&lt;br&gt;
❌ Do &lt;strong&gt;not&lt;/strong&gt; use the default GitHub SSH URL&lt;/p&gt;

&lt;p&gt;Instead, clone using the alias:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone git@github.com-myapp:your-org/myapp.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;github.com-myapp&lt;/code&gt; matches the SSH config&lt;/li&gt;
&lt;li&gt;SSH selects the correct deploy key&lt;/li&gt;
&lt;li&gt;The server can access &lt;strong&gt;only this repository&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 5 — Consistent Naming (Strongly Recommended)
&lt;/h2&gt;

&lt;p&gt;To avoid confusion, keep names aligned:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SSH alias&lt;/td&gt;
&lt;td&gt;&lt;code&gt;github.com-myapp&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Key file&lt;/td&gt;
&lt;td&gt;&lt;code&gt;id_ed25519_myapp&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repo name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;myapp&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This becomes critical when managing many applications on the same server.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Setup Is Secure
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No personal GitHub accounts on servers&lt;/li&gt;
&lt;li&gt;No long-lived access tokens&lt;/li&gt;
&lt;li&gt;No shared SSH keys&lt;/li&gt;
&lt;li&gt;One compromised key affects only one repo&lt;/li&gt;
&lt;li&gt;Access can be revoked instantly from GitHub&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is &lt;strong&gt;least-privilege access&lt;/strong&gt;, applied correctly.&lt;/p&gt;




&lt;h2&gt;
  
  
  How This Fits Into a Secure Deployment Workflow
&lt;/h2&gt;

&lt;p&gt;In a hardened server setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Admin users clone and build apps&lt;/li&gt;
&lt;li&gt;Service users never touch Git&lt;/li&gt;
&lt;li&gt;Deploy keys are read-only&lt;/li&gt;
&lt;li&gt;Runtime environments are isolated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This keeps &lt;strong&gt;code access, runtime access, and system access&lt;/strong&gt; clearly separated.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Secure infrastructure is about &lt;strong&gt;boundaries&lt;/strong&gt;, not convenience.&lt;/p&gt;

&lt;p&gt;Using deploy keys with SSH aliases is slightly more work upfront —&lt;br&gt;
but it prevents an entire class of security failures later.&lt;/p&gt;

&lt;p&gt;If your production server ever needs GitHub access,&lt;br&gt;
&lt;strong&gt;this is the correct way to do it&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>github</category>
      <category>git</category>
      <category>ssh</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>Introduction to Satellite Remote Sensing and Vegetation Indices</title>
      <dc:creator>Yousuf Basir</dc:creator>
      <pubDate>Sat, 30 Aug 2025 18:54:51 +0000</pubDate>
      <link>https://dev.to/yousufbasir/introduction-to-satellite-remote-sensing-and-vegetation-indices-5gb5</link>
      <guid>https://dev.to/yousufbasir/introduction-to-satellite-remote-sensing-and-vegetation-indices-5gb5</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Note: This article was generated with the help of ChatGPT (an AI by OpenAI). The content is based on my questions and the AI’s explanations, structured into an article format.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Remote sensing is the science of observing Earth from space using satellites. Instead of needing huge cameras in orbit, satellites use special sensors that record reflected sunlight or emitted energy across different parts of the electromagnetic spectrum (visible, infrared, thermal, radar). These measurements are then processed into images and indices that help us understand vegetation, water, soil, climate, and urban areas.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛰️ How Do Satellites Capture Images from Space?
&lt;/h2&gt;

&lt;p&gt;You might wonder: how can satellites, orbiting hundreds of kilometers above Earth, capture detailed images without giant lenses?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pushbroom Scanning&lt;/strong&gt;: Instead of taking a single photo, satellites scan the ground line by line as they move, building images piece by piece.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sensitive Detectors&lt;/strong&gt;: They use advanced CCD/CMOS arrays to record different wavelengths (blue, red, near-infrared, shortwave infrared, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Telescopic Optics&lt;/strong&gt;: Satellites rely on long focal lengths and narrow fields of view, not massive lenses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Synthetic Aperture Radar (SAR)&lt;/strong&gt;: Radar satellites simulate very large antennas using satellite motion, producing high-resolution images even at night or through clouds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These techniques make it possible to achieve ground resolutions from &lt;strong&gt;30 meters (Landsat)&lt;/strong&gt; to &lt;strong&gt;as fine as 30 cm (WorldView satellites)&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌱 Key Vegetation and Water Indices
&lt;/h2&gt;

&lt;p&gt;Satellite sensors capture multiple spectral bands. By combining them in ratios, scientists create &lt;strong&gt;indices&lt;/strong&gt; that highlight specific land surface properties. Some of the most important ones are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NDVI (Normalized Difference Vegetation Index)&lt;/strong&gt; → Measures plant greenness and vigor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EVI (Enhanced Vegetation Index)&lt;/strong&gt; → Similar to NDVI but reduces atmospheric effects; good for dense forests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LAI (Leaf Area Index)&lt;/strong&gt; → Estimates how much leaf surface covers the ground; linked to biomass and photosynthesis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LSWI (Land Surface Water Index)&lt;/strong&gt; → Indicates soil and vegetation water content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NDMI (Normalized Difference Moisture Index)&lt;/strong&gt; → Detects canopy moisture and stress.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NDWI (Normalized Difference Water Index)&lt;/strong&gt; → Maps open water bodies like lakes and rivers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NBR (Normalized Burn Ratio)&lt;/strong&gt; → Detects burned or fire-damaged areas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NDBI (Normalized Difference Built-up Index)&lt;/strong&gt; → Distinguishes urban/built-up areas from vegetation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each index highlights a different aspect of the land, and combining them gives a more complete picture of ecosystems.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 What Can We Learn from These Indices?
&lt;/h2&gt;

&lt;p&gt;With time-series data (e.g., weekly averages), we can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Track &lt;strong&gt;vegetation growth and decline&lt;/strong&gt; across seasons.&lt;/li&gt;
&lt;li&gt;Estimate &lt;strong&gt;crop yield and forest biomass&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Detect &lt;strong&gt;water stress, droughts, or floods&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Monitor &lt;strong&gt;phenology&lt;/strong&gt; (start, peak, and end of growing seasons).&lt;/li&gt;
&lt;li&gt;Map &lt;strong&gt;urban expansion or deforestation&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These insights are vital for agriculture, forestry, climate research, and disaster management.&lt;/p&gt;




&lt;h2&gt;
  
  
  📡 Which Satellites Provide These Indices?
&lt;/h2&gt;

&lt;p&gt;Different sensors are optimized for different applications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Landsat (NASA/USGS)&lt;/strong&gt; → General purpose, long historical record (30 m resolution).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sentinel-2 (ESA)&lt;/strong&gt; → High-resolution (10 m), excellent for agriculture thanks to red-edge bands.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MODIS (NASA)&lt;/strong&gt; → Daily global monitoring, useful for time-series analysis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hyperspectral Sensors (e.g., PRISMA, EnMAP)&lt;/strong&gt; → Hundreds of narrow bands, ideal for detailed vegetation and geology studies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SAR Satellites (e.g., Sentinel-1, RADARSAT)&lt;/strong&gt; → Radar imaging, works day/night and through clouds.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ✅ Conclusion
&lt;/h2&gt;

&lt;p&gt;Remote sensing makes it possible to study our planet from space with incredible detail — without giant cameras. By using &lt;strong&gt;line scanning, sensitive detectors, telescopic optics, and radar techniques&lt;/strong&gt;, satellites capture the data needed to calculate powerful indices like NDVI, LSWI, and NDMI. These indices help us monitor vegetation health, water availability, soil conditions, urban growth, and environmental change.&lt;/p&gt;

</description>
      <category>geospatial</category>
      <category>remotesensing</category>
      <category>satelliteimagery</category>
      <category>gis</category>
    </item>
    <item>
      <title>A Beginner’s Guide to Satellite Data and Vegetation Indices (NDVI, EVI &amp; Beyond)</title>
      <dc:creator>Yousuf Basir</dc:creator>
      <pubDate>Tue, 26 Aug 2025 19:11:19 +0000</pubDate>
      <link>https://dev.to/yousufbasir/a-beginners-guide-to-satellite-data-and-vegetation-indices-ndvi-evi-beyond-14nn</link>
      <guid>https://dev.to/yousufbasir/a-beginners-guide-to-satellite-data-and-vegetation-indices-ndvi-evi-beyond-14nn</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Note: This article was generated with the help of ChatGPT (an AI by OpenAI). The content is based on my questions and the AI’s explanations, structured into an article format.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Satellite data analysis—often called &lt;strong&gt;Remote Sensing&lt;/strong&gt; or &lt;strong&gt;Earth Observation&lt;/strong&gt;—is a powerful way to understand our planet. From monitoring crops to tracking droughts, satellites provide a bird’s-eye view of Earth in different wavelengths of light.&lt;/p&gt;

&lt;p&gt;If you’re new, terms like &lt;strong&gt;NDVI, EVI, vegetation index, or spectral bands&lt;/strong&gt; can feel confusing. In this article, we’ll break down the essentials in a simple way.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. What Satellites Actually Capture
&lt;/h2&gt;

&lt;p&gt;Satellites don’t directly capture “NDVI images” or “vegetation maps.” Instead, they record &lt;strong&gt;reflectance&lt;/strong&gt;: how much light is reflected from Earth’s surface in different parts of the electromagnetic spectrum.&lt;/p&gt;

&lt;p&gt;Each part of the spectrum is stored as a separate &lt;strong&gt;band&lt;/strong&gt;, which is just a &lt;strong&gt;grayscale image&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blue, Green, Red bands&lt;/strong&gt; – visible light, similar to our eyes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Near Infrared (NIR)&lt;/strong&gt; – invisible to us, but plants reflect it strongly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shortwave Infrared (SWIR)&lt;/strong&gt; – useful for soil and water content.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Each band looks black-and-white on its own, but carries unique information about the surface.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. From Grayscale to Color Composites
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3nfz0etq6wgcxkug7bkl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3nfz0etq6wgcxkug7bkl.png" alt=" " width="800" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A single band is grayscale, but by combining bands, we can create &lt;strong&gt;color composites&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;True Color (RGB):&lt;/strong&gt; Red → Red channel, Green → Green channel, Blue → Blue channel → looks like a normal photo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;False Color:&lt;/strong&gt; Swap in NIR for Red → vegetation appears bright red, making it easy to detect.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These composites are not “photos” in the traditional sense—they are &lt;strong&gt;visualizations of band data&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. What Are Vegetation Indices?
&lt;/h2&gt;

&lt;p&gt;Vegetation indices are &lt;strong&gt;mathematical formulas&lt;/strong&gt; that combine bands to highlight plant health.&lt;/p&gt;

&lt;h3&gt;
  
  
  NDVI (Normalized Difference Vegetation Index)
&lt;/h3&gt;

&lt;p&gt;Formula: (NIR – Red) / (NIR + Red)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Healthy, dense vegetation: values close to &lt;strong&gt;+1&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Sparse vegetation: &lt;strong&gt;0.2 – 0.5&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Bare soil: near &lt;strong&gt;0.0&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Water/snow/cloud: &lt;strong&gt;negative values&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  EVI (Enhanced Vegetation Index)
&lt;/h3&gt;

&lt;p&gt;A refined version of NDVI, correcting for soil and atmosphere, useful in dense forests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Indices
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NDWI:&lt;/strong&gt; water detection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SAVI:&lt;/strong&gt; adjusts NDVI for soil background&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chlorophyll Indices (GCI, MCARI):&lt;/strong&gt; measure plant pigments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 These indices aren’t captured by satellites—they are &lt;strong&gt;calculated from bands&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Why Do NDVI Maps Look So Colorful?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feypqkcxpnue01k57bgzm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feypqkcxpnue01k57bgzm.png" alt=" " width="782" height="697"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Raw NDVI is just a grayscale layer (values from –1 to +1). To make interpretation easier, software assigns &lt;strong&gt;color palettes&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Green&lt;/strong&gt; = healthy vegetation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yellow/Orange&lt;/strong&gt; = moderate vegetation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Red&lt;/strong&gt; = stressed or bare land&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blue/Black&lt;/strong&gt; = water or no data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;strong&gt;colors are symbolic&lt;/strong&gt;, not physical—they’re chosen by analysts for readability.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. One Sensor, Many Indices
&lt;/h2&gt;

&lt;p&gt;A common beginner misunderstanding is: &lt;em&gt;“Do satellites have one sensor for NDVI, another for EVI, etc.?”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The answer: &lt;strong&gt;No.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A single satellite instrument (like Sentinel-2’s MSI or Landsat’s OLI) captures multiple &lt;strong&gt;bands&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Indices are then &lt;strong&gt;derived mathematically&lt;/strong&gt; using different band combinations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means the same dataset can generate NDVI, EVI, NDWI, and many others.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Why This Matters
&lt;/h2&gt;

&lt;p&gt;Understanding these basics helps you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Interpret satellite maps correctly (colors ≠ raw reality).&lt;/li&gt;
&lt;li&gt;Choose the right index for your application (NDVI for crops, NDWI for water, etc.).&lt;/li&gt;
&lt;li&gt;Avoid misconceptions (indices are not directly captured, but derived).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  7. Next Steps for Beginners
&lt;/h2&gt;

&lt;p&gt;If you’re starting in geo data analysis:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Learn Remote Sensing Basics&lt;/strong&gt; – bands, resolutions, reflectance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explore Free Tools&lt;/strong&gt; – QGIS (desktop) or Google Earth Engine (cloud).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Practice NDVI&lt;/strong&gt; – compute it using Sentinel-2 or Landsat data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Move to Coding&lt;/strong&gt; – Python libraries (rasterio, geopandas, earthengine-api).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specialize&lt;/strong&gt; – agriculture, forestry, hydrology, or climate studies.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;At its core, satellite data is &lt;strong&gt;grayscale reflectance measurements across spectral bands&lt;/strong&gt;. Indices like NDVI and EVI are not directly sensed—they’re &lt;strong&gt;derived by combining bands&lt;/strong&gt;. The colorful maps we see are just &lt;strong&gt;visual representations&lt;/strong&gt; of these numbers.&lt;/p&gt;

&lt;p&gt;By grasping this foundation, you can start making sense of Earth observation data and unlock its potential for real-world applications.&lt;/p&gt;

</description>
      <category>satellitedata</category>
      <category>earthengine</category>
      <category>gis</category>
      <category>qgis</category>
    </item>
    <item>
      <title>Managing Multiple SSH Servers Across Windows &amp; macOS with SSH Config &amp; Tmux</title>
      <dc:creator>Yousuf Basir</dc:creator>
      <pubDate>Wed, 20 Aug 2025 11:06:51 +0000</pubDate>
      <link>https://dev.to/yousufbasir/managing-multiple-ssh-servers-across-windows-macos-with-ssh-config-tmux-21ea</link>
      <guid>https://dev.to/yousufbasir/managing-multiple-ssh-servers-across-windows-macos-with-ssh-config-tmux-21ea</guid>
      <description>&lt;p&gt;If you work with multiple servers — some requiring &lt;code&gt;.pem&lt;/code&gt; keypairs and others with password authentication — you know how quickly it becomes messy. Add in the fact that you might switch between &lt;strong&gt;Windows at home&lt;/strong&gt; and &lt;strong&gt;macOS at work&lt;/strong&gt;, and suddenly managing SSH connections can feel like juggling knives.&lt;/p&gt;

&lt;p&gt;In this article, I’ll show you how to organize your SSH access across &lt;strong&gt;both Windows and macOS&lt;/strong&gt; using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenSSH (built-in on both OS)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;~/.ssh/config&lt;/code&gt; (for managing profiles)&lt;/li&gt;
&lt;li&gt;Multiplexing (to speed up connections)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tmux&lt;/code&gt; (to keep sessions alive even if your laptop disconnects)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s dive in 👇&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Install OpenSSH
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;macOS&lt;/strong&gt; → Already installed, just open Terminal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows 10/11&lt;/strong&gt; → OpenSSH is included, but if missing:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Settings → Apps → Optional Features → Add a Feature → OpenSSH Client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh user@server-ip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Use &lt;code&gt;~/.ssh/config&lt;/code&gt; for Profiles
&lt;/h2&gt;

&lt;p&gt;Instead of typing long commands, store servers in a config file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Windows path&lt;/strong&gt; → &lt;code&gt;C:\Users\&amp;lt;YourUser&amp;gt;\.ssh\config&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;macOS/Linux path&lt;/strong&gt; → &lt;code&gt;~/.ssh/config&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Server with PEM key
Host project-server
    HostName 203.0.113.10
    User ubuntu
    IdentityFile ~/.ssh/project-server.pem

# Server with password login
Host db-server
    HostName 198.51.100.20
    User root
    PreferredAuthentications password

# GitHub shortcut
Host github.com
    User git
    IdentityFile ~/.ssh/github_id_rsa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can connect with simple commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh project-server
ssh db-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Handling Password Servers
&lt;/h2&gt;

&lt;p&gt;Configs don’t store passwords for security reasons. Options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On macOS/Linux:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  brew &lt;span class="nb"&gt;install &lt;/span&gt;hudochenkov/sshpass/sshpass
  sshpass &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'mypassword'&lt;/span&gt; ssh db-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Or better: set up &lt;strong&gt;key-based login&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  ssh-copy-id user@db-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Now you won’t need the password every time 🚀)&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Speed Up Connections with Multiplexing
&lt;/h2&gt;

&lt;p&gt;Add this to your &lt;code&gt;~/.ssh/config&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host *
    ControlMaster auto
    ControlPath ~/.ssh/cm-%r@%h:%p
    ControlPersist 10m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ First connection = normal login&lt;br&gt;
✅ Next connections (within 10 mins) = instant, no re-auth&lt;/p&gt;


&lt;h2&gt;
  
  
  5. Keep Sessions Alive with &lt;code&gt;tmux&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;On any server, install &lt;code&gt;tmux&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;tmux   &lt;span class="c"&gt;# Ubuntu/Debian&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;tmux       &lt;span class="c"&gt;# macOS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tmux new &lt;span class="nt"&gt;-s&lt;/span&gt; mysession      &lt;span class="c"&gt;# start session&lt;/span&gt;
Ctrl+b d                   &lt;span class="c"&gt;# detach but keep running&lt;/span&gt;
tmux attach &lt;span class="nt"&gt;-t&lt;/span&gt; mysession   &lt;span class="c"&gt;# reattach later&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your scripts and processes survive even if your laptop disconnects.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Sync Configs Between Machines
&lt;/h2&gt;

&lt;p&gt;To keep everything consistent across &lt;strong&gt;Windows and macOS&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store &lt;code&gt;~/.ssh/config&lt;/code&gt; and &lt;code&gt;.pem&lt;/code&gt; files in a &lt;strong&gt;private Git repo&lt;/strong&gt; or &lt;strong&gt;encrypted cloud storage&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Copy or symlink them into &lt;code&gt;~/.ssh&lt;/code&gt; on each machine.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ✅ Final Thoughts
&lt;/h2&gt;

&lt;p&gt;By combining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SSH config&lt;/strong&gt; for organizing servers,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiplexing&lt;/strong&gt; for faster connections, and&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tmux&lt;/strong&gt; for persistent sessions,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…you get a &lt;strong&gt;portable, reliable, and cross-platform SSH workflow&lt;/strong&gt; that works seamlessly on &lt;strong&gt;Windows and macOS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;No more remembering IPs, juggling &lt;code&gt;.pem&lt;/code&gt; files, or retyping passwords. Just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh project-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you’re in! 🎉&lt;/p&gt;




&lt;p&gt;💬 What about you? Do you use a GUI-based SSH manager (like Termius or MobaXterm), or do you prefer CLI setups like this one?&lt;/p&gt;

</description>
      <category>ssh</category>
      <category>linux</category>
      <category>pem</category>
      <category>tmux</category>
    </item>
    <item>
      <title>Fixing “Service Account Key Creation is Disabled” in Google Cloud Console</title>
      <dc:creator>Yousuf Basir</dc:creator>
      <pubDate>Sun, 10 Aug 2025 12:03:07 +0000</pubDate>
      <link>https://dev.to/yousufbasir/fixing-service-account-key-creation-is-disabled-in-google-cloud-console-3iac</link>
      <guid>https://dev.to/yousufbasir/fixing-service-account-key-creation-is-disabled-in-google-cloud-console-3iac</guid>
      <description>&lt;p&gt;When working with Google Cloud and products like &lt;strong&gt;Google Earth Engine&lt;/strong&gt;, you may run into this frustrating error:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Service account key creation is disabled&lt;/strong&gt;&lt;br&gt;
An organisation policy that blocks service account key creation has been enforced on your organisation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This guide walks through &lt;strong&gt;why this happens&lt;/strong&gt;, &lt;strong&gt;how to diagnose it&lt;/strong&gt;, and the &lt;strong&gt;exact steps to fix it&lt;/strong&gt; — based on a real-world scenario.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Understanding the Problem&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Google Cloud service account keys allow code running outside GCP (like a backend server) to authenticate securely.&lt;br&gt;
However, they also pose a security risk if leaked.&lt;/p&gt;

&lt;p&gt;Many organisations block key creation by enforcing an &lt;strong&gt;Organisation Policy Constraint&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Legacy constraint:&lt;/strong&gt;
&lt;code&gt;iam.disableServiceAccountKeyCreation&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managed constraint (newer):&lt;/strong&gt;
&lt;code&gt;iam.managed.disableServiceAccountKeyCreation&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When either one is &lt;strong&gt;active&lt;/strong&gt; and enforced at the organisation level, all projects inherit the restriction.&lt;br&gt;
This means even if you are a &lt;strong&gt;Project Owner&lt;/strong&gt;, you can’t create JSON keys until the policy is overridden.&lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;Why Both Policies Matter&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Google is migrating from the &lt;strong&gt;legacy constraint&lt;/strong&gt; to the &lt;strong&gt;managed constraint&lt;/strong&gt;, but during the transition:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Both constraints are evaluated&lt;/strong&gt; for security.&lt;/li&gt;
&lt;li&gt;Disabling just one is &lt;strong&gt;not enough&lt;/strong&gt; — if the other remains active, the block continues.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s why some users disable the new constraint, but still see the error when creating keys — the legacy one is still active.&lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;Diagnosing the Issue&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Switch to your &lt;strong&gt;project&lt;/strong&gt; in the Google Cloud Console.&lt;/li&gt;
&lt;li&gt;Navigate to:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   IAM &amp;amp; Admin → Organisation policies
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Search for &lt;strong&gt;"Disable service account key creation"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;If you see &lt;strong&gt;two entries&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;One with &lt;code&gt;.managed&lt;/code&gt; in the ID (new)&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;One without &lt;code&gt;.managed&lt;/code&gt; (legacy)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check the &lt;strong&gt;Enforcement state&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If &lt;strong&gt;Active&lt;/strong&gt;, the policy is blocking you.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;Fixing the Policy&lt;/strong&gt;
&lt;/h2&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1 — Make Sure You Have the Right Role&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Organisation Policy Administrator&lt;/strong&gt; (&lt;code&gt;roles/orgpolicy.policyAdmin&lt;/code&gt;)
&lt;em&gt;or&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Organisation Administrator&lt;/strong&gt; (&lt;code&gt;roles/resourcemanager.organizationAdmin&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you only have project-level roles, you won’t be able to override an inherited policy.&lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;Step 2 — Override the Managed Constraint&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Switch to your &lt;strong&gt;project&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Go to:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   IAM &amp;amp; Admin → Organisation policies
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Search for:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   iam.managed.disableServiceAccountKeyCreation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Click it → &lt;strong&gt;Manage policy&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Choose:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Override parent’s policy&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;Enforcement&lt;/strong&gt; to &lt;strong&gt;Off&lt;/strong&gt;.

&lt;ol&gt;
&lt;li&gt;Save.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;Step 3 — Override the Legacy Constraint&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Still in the &lt;strong&gt;project&lt;/strong&gt; view, search for:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   iam.disableServiceAccountKeyCreation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Click it → &lt;strong&gt;Manage policy&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Choose:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Override parent’s policy&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;Enforcement&lt;/strong&gt; to &lt;strong&gt;Off&lt;/strong&gt;.

&lt;ol&gt;
&lt;li&gt;Save.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;Step 4 — Ensure Project Access&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Even with the policy disabled, you must have the right project-level permissions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Service Account Admin&lt;/strong&gt; (&lt;code&gt;roles/iam.serviceAccountAdmin&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;or &lt;strong&gt;Editor&lt;/strong&gt; (&lt;code&gt;roles/editor&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without these, you may see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Missing permissions: iam.serviceAccounts.list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;when trying to open the Service Accounts page.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Creating the Service Account Key&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Once both constraints are set to &lt;strong&gt;Not enforced&lt;/strong&gt; for your project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   IAM &amp;amp; Admin → Service accounts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Open your service account.&lt;/li&gt;
&lt;li&gt;Go to the &lt;strong&gt;Keys&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Click:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Add key → Create new key → JSON
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Download and store the key securely.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Security Best Practices&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Never&lt;/strong&gt; commit your JSON key to GitHub or public storage.&lt;/li&gt;
&lt;li&gt;Store it in a secure secret manager.&lt;/li&gt;
&lt;li&gt;Rotate or delete unused keys regularly.&lt;/li&gt;
&lt;li&gt;Consider using &lt;strong&gt;Workload Identity Federation&lt;/strong&gt; to avoid storing keys entirely.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The “Service account key creation is disabled” error is usually caused by organisation policies.&lt;br&gt;
The tricky part is that &lt;strong&gt;both&lt;/strong&gt; the legacy and the managed constraint can block you — disabling only one won’t work.&lt;/p&gt;

&lt;p&gt;By:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Overriding &lt;strong&gt;both constraints&lt;/strong&gt; at the project level, and&lt;/li&gt;
&lt;li&gt;Ensuring you have the right &lt;strong&gt;project permissions&lt;/strong&gt;,&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;…you can generate the JSON key and move forward with your development.&lt;/p&gt;

</description>
      <category>googlecloud</category>
      <category>serviceaccount</category>
      <category>aws</category>
    </item>
    <item>
      <title>Setting Up Redis with Docker: A Step-by-Step Guide</title>
      <dc:creator>Yousuf Basir</dc:creator>
      <pubDate>Fri, 14 Mar 2025 19:03:25 +0000</pubDate>
      <link>https://dev.to/yousufbasir/setting-up-redis-with-docker-a-step-by-step-guide-3g2h</link>
      <guid>https://dev.to/yousufbasir/setting-up-redis-with-docker-a-step-by-step-guide-3g2h</guid>
      <description>&lt;p&gt;Redis, an open-source, in-memory data structure store, is widely used as a database, cache, message broker, and streaming engine. In this guide, I'll walk you through deploying Redis using Docker, ensuring data persistence and remote connectivity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before starting, ensure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A VPS or server with Linux installed&lt;/li&gt;
&lt;li&gt;Docker installed and running&lt;/li&gt;
&lt;li&gt;Basic familiarity with command line interfaces&lt;/li&gt;
&lt;li&gt;SSH access to your server&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Pull the Redis Image
&lt;/h2&gt;

&lt;p&gt;First, let's download the official Redis image from Docker Hub:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull redis:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This fetches the latest Redis image. If you need a specific version, replace &lt;code&gt;latest&lt;/code&gt; with the version number (e.g., &lt;code&gt;redis:7.0&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Create a Directory for Data Persistence
&lt;/h2&gt;

&lt;p&gt;To ensure your Redis data survives container restarts or removal, create a directory for data persistence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/redis_data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Run the Redis Container
&lt;/h2&gt;

&lt;p&gt;Now, deploy Redis with appropriate settings for security and persistence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; redis &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 6379:6379 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; ~/redis_data:/data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  redis:latest &lt;span class="se"&gt;\&lt;/span&gt;
  redis-server &lt;span class="nt"&gt;--requirepass&lt;/span&gt; your_strong_redis_password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's examine each part of this command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-d&lt;/code&gt;: Runs the container in detached mode&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--name redis&lt;/code&gt;: Names the container "redis" for easy reference&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-p 6379:6379&lt;/code&gt;: Maps the container's Redis port to the host's port&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-v ~/redis_data:/data&lt;/code&gt;: Mounts the host directory for data persistence&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--restart always&lt;/code&gt;: Ensures automatic restart after system reboots&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;redis-server --requirepass your_strong_redis_password&lt;/code&gt;: Starts Redis with password protection&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Verify the Installation
&lt;/h2&gt;

&lt;p&gt;To confirm Redis is running properly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see your Redis container in the list of running containers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Connecting to Redis
&lt;/h2&gt;

&lt;p&gt;To connect to your Redis instance locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; redis redis-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once connected, authenticate using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AUTH your_strong_redis_password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For remote connections, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;redis-cli &lt;span class="nt"&gt;-h&lt;/span&gt; your_server_ip &lt;span class="nt"&gt;-p&lt;/span&gt; 6379 &lt;span class="nt"&gt;-a&lt;/span&gt; your_strong_redis_password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or programmatically with a Redis URI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;redis://default:your_strong_redis_password@your_server_ip:6379
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: Setting Up Persistence
&lt;/h2&gt;

&lt;p&gt;Redis offers different persistence options. By default, the Redis Docker image uses the RDB (Redis Database) persistence model, which takes snapshots of your dataset at specified intervals.&lt;/p&gt;

&lt;p&gt;To enable AOF (Append Only File) persistence for more durability, modify your run command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; redis &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 6379:6379 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; ~/redis_data:/data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  redis:latest &lt;span class="se"&gt;\&lt;/span&gt;
  redis-server &lt;span class="nt"&gt;--requirepass&lt;/span&gt; your_strong_redis_password &lt;span class="nt"&gt;--appendonly&lt;/span&gt; &lt;span class="nb"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--appendonly yes&lt;/code&gt; option enables AOF persistence, which logs every write operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Configuring Redis with a Custom Configuration File
&lt;/h2&gt;

&lt;p&gt;For more advanced configurations, create a custom Redis configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/redis_config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a file named &lt;code&gt;redis.conf&lt;/code&gt; in this directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano ~/redis_config/redis.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add your configuration settings, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;requirepass your_strong_redis_password
appendonly yes
maxmemory 256mb
maxmemory-policy allkeys-lru
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run Redis with your custom configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; redis &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 6379:6379 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; ~/redis_data:/data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; ~/redis_config/redis.conf:/usr/local/etc/redis/redis.conf &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  redis:latest &lt;span class="se"&gt;\&lt;/span&gt;
  redis-server /usr/local/etc/redis/redis.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 8: Security Recommendations
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Network Security&lt;/strong&gt;: Restrict access to port 6379
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   ufw allow from trusted_ip_address to any port 6379
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Strong Authentication&lt;/strong&gt;: Use complex passwords for Redis&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Binding&lt;/strong&gt;: In production environments, consider binding Redis to localhost and using an SSH tunnel for remote connections&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Regular Backups&lt;/strong&gt;: Set up automated backups with a cron job&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   crontab &lt;span class="nt"&gt;-e&lt;/span&gt;
   &lt;span class="c"&gt;# Add this line to create daily backups&lt;/span&gt;
   0 3 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; docker &lt;span class="nb"&gt;exec &lt;/span&gt;redis redis-cli &lt;span class="nt"&gt;-a&lt;/span&gt; your_strong_redis_password SAVE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 9: Monitoring Redis
&lt;/h2&gt;

&lt;p&gt;To monitor your Redis instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; redis redis-cli &lt;span class="nt"&gt;-a&lt;/span&gt; your_strong_redis_password INFO
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This provides comprehensive information about your Redis server, including memory usage, client connections, and persistence status.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You've successfully deployed Redis using Docker with data persistence and remote connectivity. This containerized approach offers flexibility, isolation, and easy management of your Redis instance.&lt;/p&gt;

&lt;p&gt;Whether you're using Redis as a cache, message broker, or primary database, this setup provides a solid foundation for your applications.&lt;/p&gt;

&lt;p&gt;Remember to adjust configurations based on your specific requirements and regularly monitor your Redis instance for optimal performance.&lt;/p&gt;

&lt;p&gt;Happy caching!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Note: Always replace placeholder values like &lt;code&gt;your_strong_redis_password&lt;/code&gt; and &lt;code&gt;your_server_ip&lt;/code&gt; with your actual secure credentials and server information.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>redis</category>
      <category>docker</category>
    </item>
    <item>
      <title>Installing MongoDB Using Docker: A Complete Guide</title>
      <dc:creator>Yousuf Basir</dc:creator>
      <pubDate>Fri, 14 Mar 2025 19:02:21 +0000</pubDate>
      <link>https://dev.to/yousufbasir/installing-mongodb-using-docker-a-complete-guide-fm3</link>
      <guid>https://dev.to/yousufbasir/installing-mongodb-using-docker-a-complete-guide-fm3</guid>
      <description>&lt;p&gt;Docker provides an excellent way to run MongoDB without installing it directly on your system. This approach offers better isolation, easier version management, and simpler setup. In this guide, I'll walk you through setting up MongoDB with Docker, including common pitfalls and their solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Docker installed on your system&lt;/li&gt;
&lt;li&gt;Basic knowledge of terminal/command line&lt;/li&gt;
&lt;li&gt;Root or sudo access to your machine&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Pull the MongoDB Image
&lt;/h2&gt;

&lt;p&gt;First, pull the latest MongoDB image from Docker Hub:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull mongo:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command downloads the official MongoDB image maintained by MongoDB, Inc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Create Directories for Persistent Storage
&lt;/h2&gt;

&lt;p&gt;Before running MongoDB, you need to create directories for persistent data storage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/mongodb_data
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/mongodb_backup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Set Proper Permissions
&lt;/h2&gt;

&lt;p&gt;This is a critical step that many guides miss! MongoDB inside the container runs as a non-root user (typically with UID 999), and it needs proper permissions to write to the mounted volumes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set ownership to the MongoDB user (typically 999:999 in the container)&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 999:999 ~/mongodb_data
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 999:999 ~/mongodb_backup

&lt;span class="c"&gt;# Set restrictive permissions for security&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;700 ~/mongodb_data
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;700 ~/mongodb_backup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Skipping this step is a common cause of authentication failures and connection issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Run MongoDB Container
&lt;/h2&gt;

&lt;p&gt;Now, run the MongoDB container with the following parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; mongodb &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 27017:27017 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; ~/mongodb_data:/data/db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; ~/mongodb_backup:/backup &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;MONGO_INITDB_ROOT_USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;MONGO_INITDB_ROOT_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;yoursecurepassword &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\&lt;/span&gt;
  mongo:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down this command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-d&lt;/code&gt;: Runs the container in detached mode (background)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--name mongodb&lt;/code&gt;: Names the container "mongodb" for easy reference&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-p 27017:27017&lt;/code&gt;: Maps the container's port 27017 to the host's port 27017&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-v ~/mongodb_data:/data/db&lt;/code&gt;: Mounts the local directory for persistent data storage&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-v ~/mongodb_backup:/backup&lt;/code&gt;: Mounts a directory for database backups&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-e MONGO_INITDB_ROOT_USERNAME=admin&lt;/code&gt;: Sets the root username&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-e MONGO_INITDB_ROOT_PASSWORD=yoursecurepassword&lt;/code&gt;: Sets the root password&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--restart always&lt;/code&gt;: Ensures the container restarts automatically with the system&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mongo:latest&lt;/code&gt;: Specifies the image to use&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 5: Verify the Installation
&lt;/h2&gt;

&lt;p&gt;Check if the container is running properly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps | &lt;span class="nb"&gt;grep &lt;/span&gt;mongodb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see your MongoDB container in the list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Connect to MongoDB
&lt;/h2&gt;

&lt;p&gt;Connect to your MongoDB instance using the MongoDB shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; mongodb mongosh admin &lt;span class="nt"&gt;--username&lt;/span&gt; admin &lt;span class="nt"&gt;--password&lt;/span&gt; yoursecurepassword &lt;span class="nt"&gt;--authenticationDatabase&lt;/span&gt; admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you successfully connect, you'll see the MongoDB shell prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting Common Issues
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Authentication Failed Error
&lt;/h3&gt;

&lt;p&gt;If you see &lt;code&gt;MongoServerError: Authentication failed&lt;/code&gt;, check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Whether you're using the correct username and password&lt;/li&gt;
&lt;li&gt;If the container has fully initialized (check logs with &lt;code&gt;docker logs mongodb&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The permissions on your data directory&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Permission Issues with Mounted Volumes
&lt;/h3&gt;

&lt;p&gt;This is the most common problem when setting up MongoDB with Docker. If MongoDB can't write to the mounted volumes properly, it will fail to initialize or authenticate.&lt;/p&gt;

&lt;p&gt;The solution is to correctly set ownership and permissions as shown in Step 3.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Container Won't Start or Keeps Restarting
&lt;/h3&gt;

&lt;p&gt;Check the logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker logs mongodb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for specific error messages related to permissions, port conflicts, or other initialization issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backing Up Your MongoDB Database
&lt;/h2&gt;

&lt;p&gt;To create a backup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; mongodb mongodump &lt;span class="nt"&gt;--username&lt;/span&gt; admin &lt;span class="nt"&gt;--password&lt;/span&gt; yoursecurepassword &lt;span class="nt"&gt;--authenticationDatabase&lt;/span&gt; admin &lt;span class="nt"&gt;--out&lt;/span&gt; /backup/&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y%m%d&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will create a backup in your &lt;code&gt;~/mongodb_backup&lt;/code&gt; directory with the current date as the folder name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Restoring a Backup
&lt;/h2&gt;

&lt;p&gt;To restore from a backup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; mongodb mongorestore &lt;span class="nt"&gt;--username&lt;/span&gt; admin &lt;span class="nt"&gt;--password&lt;/span&gt; yoursecurepassword &lt;span class="nt"&gt;--authenticationDatabase&lt;/span&gt; admin /backup/20250323
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;20250323&lt;/code&gt; with the actual backup folder name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Docker provides a clean, isolated environment for running MongoDB without the complexity of a direct installation. By following these steps—especially the often-overlooked permission settings—you can have a robust MongoDB instance running in minutes.&lt;/p&gt;

&lt;p&gt;The key lessons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Always set proper permissions on mounted volumes&lt;/li&gt;
&lt;li&gt;Use meaningful container names for easy management&lt;/li&gt;
&lt;li&gt;Implement persistent storage for data safety&lt;/li&gt;
&lt;li&gt;Configure authentication for security&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With this setup, you have a production-ready MongoDB instance that's easy to maintain, backup, and upgrade.&lt;/p&gt;

</description>
      <category>mongodb</category>
      <category>database</category>
      <category>docker</category>
    </item>
    <item>
      <title>Never Type Your SSH Password Again: A Complete Guide to SSH Key Authentication</title>
      <dc:creator>Yousuf Basir</dc:creator>
      <pubDate>Sun, 17 Nov 2024 10:59:56 +0000</pubDate>
      <link>https://dev.to/yousufbasir/never-type-your-ssh-password-again-a-complete-guide-to-ssh-key-authentication-58ep</link>
      <guid>https://dev.to/yousufbasir/never-type-your-ssh-password-again-a-complete-guide-to-ssh-key-authentication-58ep</guid>
      <description>&lt;p&gt;Are you tired of typing your password every time you connect to a remote server via SSH? In this guide, I'll show you how to set up SSH key-based authentication, a more secure and convenient way to connect to your servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is SSH Key Authentication?
&lt;/h2&gt;

&lt;p&gt;Before diving into the setup, let's understand what SSH keys are. Think of SSH keys as a pair of locks and keys:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have a public key (the lock) that you put on the server&lt;/li&gt;
&lt;li&gt;You keep a private key (the key) on your computer&lt;/li&gt;
&lt;li&gt;When you connect, your private key proves your identity to the server without needing a password&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step-by-Step Setup Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Generate Your SSH Key Pair
&lt;/h3&gt;

&lt;p&gt;First, you'll need to create your SSH key pair. Open your terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run this command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It will ask where to save the key (press Enter for default location)&lt;/li&gt;
&lt;li&gt;It will ask for a passphrase (press Enter twice for no passphrase)&lt;/li&gt;
&lt;li&gt;The default location is &lt;code&gt;~/.ssh/id_ed25519&lt;/code&gt; (private key) and &lt;code&gt;~/.ssh/id_ed25519.pub&lt;/code&gt; (public key)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Pro Tip&lt;/strong&gt;: Using &lt;code&gt;ed25519&lt;/code&gt; is recommended as it's more secure and modern than older alternatives like RSA.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Copy Your Public Key to the Server
&lt;/h3&gt;

&lt;p&gt;There are two ways to do this:&lt;/p&gt;

&lt;h4&gt;
  
  
  Method 1: Using ssh-copy-id (Recommended)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-copy-id username@remote_host
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the easiest method as it handles everything automatically.&lt;/p&gt;

&lt;h4&gt;
  
  
  Method 2: Manual Copy
&lt;/h4&gt;

&lt;p&gt;If &lt;code&gt;ssh-copy-id&lt;/code&gt; isn't available, you can do it manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_ed25519.pub | ssh username@remote_host &lt;span class="s2"&gt;"mkdir -p ~/.ssh &amp;amp;&amp;amp; cat &amp;gt;&amp;gt; ~/.ssh/authorized_keys"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Set Proper Permissions
&lt;/h3&gt;

&lt;p&gt;SSH is particular about security permissions. On the remote server, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;700 ~/.ssh
&lt;span class="nb"&gt;chmod &lt;/span&gt;600 ~/.ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;If you're still being prompted for a password, check these common issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;File Permissions&lt;/strong&gt;: Incorrect permissions are a common cause. Double-check them:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; ~/.ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SSH Server Configuration&lt;/strong&gt;: Ensure key authentication is enabled in &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   PubkeyAuthentication yes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SELinux/AppArmor&lt;/strong&gt;: If you're using these security systems, they might be blocking key authentication.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Security Best Practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use Strong Keys&lt;/strong&gt;: Always use ED25519 or RSA with at least 4096 bits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protect Your Private Key&lt;/strong&gt;: Never share your private key or upload it anywhere&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consider Using a Passphrase&lt;/strong&gt;: For additional security, add a passphrase to your key&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regular Key Rotation&lt;/strong&gt;: Consider generating new keys periodically&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Additional Tips
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Creating Configuration Shortcuts
&lt;/h3&gt;

&lt;p&gt;You can make SSH even more convenient by adding entries to your &lt;code&gt;~/.ssh/config&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host myserver
    HostName server.example.com
    User username
    IdentityFile ~/.ssh/id_ed25519
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can simply type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh myserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using SSH Agent
&lt;/h3&gt;

&lt;p&gt;If you do use a passphrase, you can avoid typing it repeatedly by using ssh-agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;ssh-agent&lt;span class="si"&gt;)&lt;/span&gt;
ssh-add ~/.ssh/id_ed25519
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Setting up SSH key authentication is a one-time investment that pays off in both security and convenience. No more password prompts, and you get better security! It's a win-win situation.&lt;/p&gt;

&lt;p&gt;Remember: while this setup is more convenient, it's crucial to keep your private key secure. If someone gets access to your private key, they can access all servers that trust that key.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Last updated: November 2024&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ssh</category>
    </item>
    <item>
      <title>Allow remote access to postgresql database</title>
      <dc:creator>Yousuf Basir</dc:creator>
      <pubDate>Sat, 22 Jun 2024 15:35:33 +0000</pubDate>
      <link>https://dev.to/yousufbasir/allow-remote-access-to-postgresql-database-mho</link>
      <guid>https://dev.to/yousufbasir/allow-remote-access-to-postgresql-database-mho</guid>
      <description>&lt;p&gt;Allowing remote access to your PostgreSQL database can be necessary for various reasons, such as connecting from different servers or enabling remote management. &lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Modify the PostgreSQL Configuration File
&lt;/h3&gt;

&lt;p&gt;First, you need to modify the PostgreSQL configuration file to allow connections from remote hosts. This file is typically located in &lt;code&gt;/etc/postgresql/&amp;lt;version&amp;gt;/main/postgresql.conf&lt;/code&gt;. Replace &lt;code&gt;&amp;lt;version&amp;gt;&lt;/code&gt; with your PostgreSQL version number.&lt;/p&gt;

&lt;p&gt;For example, if you are using PostgreSQL version 13, you would run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/postgresql/13/main/postgresql.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Locate the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#listen_addresses = 'localhost'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uncomment this line and change &lt;code&gt;'localhost'&lt;/code&gt; to &lt;code&gt;'*'&lt;/code&gt; to allow connections from any IP address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;listen_addresses = '*'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Configure Client Authentication
&lt;/h3&gt;

&lt;p&gt;Next, you need to configure PostgreSQL to accept remote connections by editing the &lt;code&gt;pg_hba.conf&lt;/code&gt; file, which controls client authentication.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;pg_hba.conf&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/postgresql/13/main/pg_hba.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following line at the end of the file to allow connections from any IP address using MD5 password authentication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;host    all             all             0.0.0.0/0               md5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Restart PostgreSQL Service
&lt;/h3&gt;

&lt;p&gt;For the changes to take effect, you need to restart the PostgreSQL service. Use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart postgresql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Configure Your Firewall
&lt;/h3&gt;

&lt;p&gt;Ensure your firewall allows incoming connections on PostgreSQL's default port (5432). If you are using UFW (Uncomplicated Firewall), you can allow connections to this port by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 5432/tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Verify Remote Access
&lt;/h3&gt;

&lt;p&gt;To verify that your PostgreSQL database is accessible remotely, you can use a PostgreSQL client tool like &lt;code&gt;psql&lt;/code&gt; from a remote machine. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql &lt;span class="nt"&gt;-h&lt;/span&gt; &amp;lt;your_server_ip&amp;gt; &lt;span class="nt"&gt;-U&lt;/span&gt; &amp;lt;your_username&amp;gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &amp;lt;your_database&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;&amp;lt;your_server_ip&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;your_username&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;your_database&amp;gt;&lt;/code&gt; with your server’s IP address, PostgreSQL username, and database name, respectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security Considerations
&lt;/h3&gt;

&lt;p&gt;Allowing remote access to your PostgreSQL database opens up potential security risks. Here are a few best practices to mitigate these risks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use Strong Passwords:&lt;/strong&gt; Ensure that all your PostgreSQL user accounts have strong passwords.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Restrict IP Addresses:&lt;/strong&gt; Instead of allowing connections from any IP address, restrict access to specific IP addresses or ranges by modifying the &lt;code&gt;pg_hba.conf&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable SSL:&lt;/strong&gt; Configure PostgreSQL to use SSL for encrypted connections.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regular Updates:&lt;/strong&gt; Keep your PostgreSQL installation and all related packages up to date to ensure you have the latest security patches.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;By following these steps, you can configure your PostgreSQL database to allow remote connections. Always remember to balance accessibility with security to protect your data. With the proper configuration and security measures, remote access can be both convenient and safe.&lt;/p&gt;

</description>
      <category>postgres</category>
    </item>
    <item>
      <title>Setting Up Nginx with Certbot for HTTPS on Your Web Application</title>
      <dc:creator>Yousuf Basir</dc:creator>
      <pubDate>Sat, 22 Jun 2024 14:18:05 +0000</pubDate>
      <link>https://dev.to/yousufbasir/setting-up-nginx-with-certbot-for-https-on-your-web-application-n1i</link>
      <guid>https://dev.to/yousufbasir/setting-up-nginx-with-certbot-for-https-on-your-web-application-n1i</guid>
      <description>&lt;p&gt;Securing your web application with HTTPS is crucial for protecting data integrity and privacy. This guide will walk you through the steps to set up Nginx as a reverse proxy and use Certbot to obtain a free SSL certificate from Let's Encrypt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;Before you begin, ensure you have the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A domain name pointing to your server's IP address.&lt;/li&gt;
&lt;li&gt;A server running Ubuntu (or any other Linux distribution).&lt;/li&gt;
&lt;li&gt;Nginx installed on your server.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 1: Configure Nginx
&lt;/h3&gt;

&lt;p&gt;First, we need to set up Nginx to proxy requests to our web application. Open your Nginx configuration file or create a new one for your domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/nginx/sites-available/my.website.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="s"&gt;[::]:80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;my.website.com&lt;/span&gt; &lt;span class="s"&gt;www.my.website.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:5173&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_http_version&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Upgrade&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Connection&lt;/span&gt; &lt;span class="s"&gt;'upgrade'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_cache_bypass&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration listens for HTTP requests on port 80 and proxies them to your web application running on &lt;code&gt;localhost:5173&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Enable the Nginx Configuration
&lt;/h3&gt;

&lt;p&gt;Create a symbolic link to enable the configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /etc/nginx/sites-available/my.website.com /etc/nginx/sites-enabled/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test the Nginx configuration for syntax errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-t&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the test is successful, reload Nginx to apply the changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Install Certbot
&lt;/h3&gt;

&lt;p&gt;Certbot is a tool that automates the process of obtaining and renewing SSL certificates from Let's Encrypt. Install Certbot and the Nginx plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;certbot python3-certbot-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Obtain an SSL Certificate
&lt;/h3&gt;

&lt;p&gt;Run Certbot to obtain an SSL certificate and configure Nginx to use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;certbot &lt;span class="nt"&gt;--nginx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the interactive prompts. Certbot will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Detect your Nginx configuration.&lt;/li&gt;
&lt;li&gt;Allow you to select the domain you want to secure.&lt;/li&gt;
&lt;li&gt;Automatically obtain and install the SSL certificate.&lt;/li&gt;
&lt;li&gt;Modify your Nginx configuration to redirect HTTP traffic to HTTPS.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Certbot will update your Nginx configuration to something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="s"&gt;[::]:80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;my.website.com&lt;/span&gt; &lt;span class="s"&gt;www.my.website.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;301&lt;/span&gt; &lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="nv"&gt;$host$request_uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="s"&gt;[::]:443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;my.website.com&lt;/span&gt; &lt;span class="s"&gt;www.my.website.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kn"&gt;ssl_certificate&lt;/span&gt; &lt;span class="n"&gt;/etc/letsencrypt/live/my.website.com/fullchain.pem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;ssl_certificate_key&lt;/span&gt; &lt;span class="n"&gt;/etc/letsencrypt/live/my.website.com/privkey.pem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;include&lt;/span&gt; &lt;span class="n"&gt;/etc/letsencrypt/options-ssl-nginx.conf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;ssl_dhparam&lt;/span&gt; &lt;span class="n"&gt;/etc/letsencrypt/ssl-dhparams.pem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:5173&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_http_version&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Upgrade&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Connection&lt;/span&gt; &lt;span class="s"&gt;'upgrade'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_cache_bypass&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Verify HTTPS
&lt;/h3&gt;

&lt;p&gt;After Certbot completes, verify that your site is accessible via HTTPS by navigating to your website url (e.g. &lt;code&gt;https://my.website.com&lt;/code&gt; ) in your browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;You have successfully set up Nginx as a reverse proxy for your web application and secured it with an SSL certificate from Let's Encrypt using Certbot. This setup not only secures your web application but also improves its trustworthiness and SEO ranking.&lt;/p&gt;

&lt;p&gt;For further reading and additional configurations, you may refer to the following resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://jasonvan.ca/posts/deploy-next-js-app-with-nginx-let-s-encrypt-and-pm2" rel="noopener noreferrer"&gt;Deploy Next.js App With Nginx, Let's Encrypt, and PM2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://certbot.eff.org/instructions?ws=nginx&amp;amp;os=ubuntufocal" rel="noopener noreferrer"&gt;Certbot Instructions for Nginx on Ubuntu&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>nginx</category>
      <category>certbot</category>
      <category>ssl</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Automating Next.js Application Deployment with GitHub Actions</title>
      <dc:creator>Yousuf Basir</dc:creator>
      <pubDate>Thu, 28 Sep 2023 18:17:47 +0000</pubDate>
      <link>https://dev.to/yousufbasir/automating-nextjs-application-deployment-with-github-actions-38ac</link>
      <guid>https://dev.to/yousufbasir/automating-nextjs-application-deployment-with-github-actions-38ac</guid>
      <description>&lt;p&gt;Are you ready to supercharge your Next.js application deployment? With GitHub Actions, the process becomes a breeze, and I'm here to guide you through it step by step. Even if you're new to this, don't worry! We'll make it easy and fun.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Setting Up Secrets
&lt;/h3&gt;

&lt;p&gt;The first thing you need to do is head over to your GitHub repository. From there, navigate to "Secrets and Variables" under the "Actions" tab. We'll set up three secrets that will help automate our deployment:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;HOST&lt;/strong&gt;: This is your server's IP address.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;USERNAME&lt;/strong&gt;: Enter your server's username.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH_PRIVATE_KEY&lt;/strong&gt;: See step 2 and 3 &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Don't fret; it's easier than it sounds. These secrets will allow GitHub Actions to securely communicate with your server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Generating an SSH Key
&lt;/h3&gt;

&lt;p&gt;Now, let's generate an SSH key. SSH keys are like digital fingerprints that help authenticate your server and GitHub. Open your terminal and follow these simple commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/.ssh
ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; rsa &lt;span class="nt"&gt;-b&lt;/span&gt; 4096 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"youremail@email.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The -t flag is for specifying type of key. Here we are generating RSA
key

&lt;ul&gt;
&lt;li&gt;The -b flag sets the key length, which determines the key's strength. Here we are generating 4096 bit key&lt;/li&gt;
&lt;li&gt;The -c flag is is used to add a comment or label to the key. It's not required but can be helpful for identifying the key's purpose or owner.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;You'll be prompted to name your key. Let's call it &lt;strong&gt;github_action&lt;/strong&gt; and you can leave the passphrase empty.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Adding the Public Key to GitHub
&lt;/h3&gt;

&lt;p&gt;Run &lt;code&gt;ls&lt;/code&gt;, and you'll see two newly generated files: &lt;code&gt;github_action&lt;/code&gt; and &lt;code&gt;github_action.pub&lt;/code&gt;. Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;github_action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will display your private key. Copy the content and head back to your repository settings, specifically "Secrets and Variables" under "Actions." Add the private key content to the &lt;code&gt;SSH_PRIVATE_KEY&lt;/code&gt; secret.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Adding the Private Key to GitHub Secrets
&lt;/h3&gt;

&lt;p&gt;We're almost there!&lt;br&gt;
Back in your terminal, let's copy the public key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;github_action.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, go to your GitHub repository settings and select "SSH and GPG Keys" from the left navigation bar. Click "New SSH Key" and paste your public key. This step allows your server to communicate securely with your GitHub repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Authorizing Your Public Key on the Server
&lt;/h3&gt;

&lt;p&gt;Now, let's make sure your server recognizes your public key. Run this command on your server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;github_action.pub &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; authorized_keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command appends your public key to the list of authorized keys on your server, granting access for automated deployments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Creating GitHub Actions Workflow
&lt;/h3&gt;

&lt;p&gt;The final step is to automate the deployment process. Create a GitHub Actions workflow by adding an action YAML file inside the &lt;code&gt;.github/workflows&lt;/code&gt; directory in your repository.&lt;/p&gt;

&lt;p&gt;Specify the event name as "push," define the branch name, and outline the action steps in your workflow script. This tells GitHub Actions when to trigger automatic deployments.&lt;/p&gt;

&lt;p&gt;Here is an example GitHub Action for automating Next.js deployment. Customize with your repository name, branch name, application directory, and PM2 service name on your server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: Deployment Workflow

# Trigger this workflow on pushes to the specified branch
on:
  push:
    branches:
      - your-branch-name

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Install dependencies
        run: npm install

      - name: Build Next.js app
        run: npm run build

      - name: SSH Deploy
        # Use the 'appleboy/ssh-action' action for SSH deployment
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.HOST }} # Your server's IP address
          username: ${{ secrets.USERNAME }} # Your server's username
          key: ${{ secrets.SSH_PRIVATE_KEY }} # Your server's SSH private key
          script: |
            cd /path/to/your/application/directory # Specify the path to your app directory on the server
            git pull
            npm install
            npm run build
            pm2 restart your-pm2-service-name # Replace with your PM2 service name

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! 🚀&lt;/p&gt;

&lt;p&gt;From now on, whenever you push changes to your specified branch, GitHub Actions will automatically kick off the deployment process. &lt;br&gt;
Happy coding!  ☕&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>githubactions</category>
      <category>cicd</category>
      <category>react</category>
    </item>
  </channel>
</rss>
