<?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>Forem: Zed</title>
    <description>The latest articles on Forem by Zed (@zedai00).</description>
    <link>https://forem.com/zedai00</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%2F510891%2Fe8b0a59b-144b-4e88-8d2a-39d870d21a74.png</url>
      <title>Forem: Zed</title>
      <link>https://forem.com/zedai00</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/zedai00"/>
    <language>en</language>
    <item>
      <title>🚀 Automating Subdomains &amp; Databases with AWS Route 53, FastAPI, and NGINX</title>
      <dc:creator>Zed</dc:creator>
      <pubDate>Thu, 28 Aug 2025 17:37:47 +0000</pubDate>
      <link>https://forem.com/zedai00/automating-subdomains-databases-with-aws-route-53-fastapi-and-nginx-56df</link>
      <guid>https://forem.com/zedai00/automating-subdomains-databases-with-aws-route-53-fastapi-and-nginx-56df</guid>
      <description>&lt;p&gt;Managing &lt;strong&gt;multi-tenant SaaS apps&lt;/strong&gt; can get tricky.&lt;br&gt;&lt;br&gt;
Each tenant usually needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;unique subdomain&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;dedicated database&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;scalable backend&lt;/strong&gt; to handle requests
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Manually setting up subdomains and databases is painful. So… I automated it. 😎  &lt;/p&gt;

&lt;p&gt;In this guide, I’ll walk you through how I built an &lt;strong&gt;automatic subdomain + database provisioning system&lt;/strong&gt; using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS Route 53&lt;/strong&gt; → Manages subdomains
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FastAPI&lt;/strong&gt; → Handles tenant onboarding
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MySQL&lt;/strong&gt; → Stores tenant-specific data
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NGINX&lt;/strong&gt; → Routes traffic based on subdomain
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boto3&lt;/strong&gt; → Talks to AWS from Python&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's dive in! 🏊‍♂️&lt;/p&gt;


&lt;h2&gt;
  
  
  🌐 Step 1 — Automate Subdomain Creation with Route 53
&lt;/h2&gt;

&lt;p&gt;First, create a &lt;strong&gt;hosted zone&lt;/strong&gt; in AWS Route 53 for your domain.&lt;/p&gt;
&lt;h3&gt;
  
  
  Install Boto3:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;boto3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Python Function to Create Subdomain:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_subdomain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subdomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;route53&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;route53&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ap-south-1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;hosted_zone_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ZXXXXXXXXXXX&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# Replace with your Hosted Zone ID
&lt;/span&gt;    &lt;span class="n"&gt;domain_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;route53&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;change_resource_record_sets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;HostedZoneId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;hosted_zone_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ChangeBatch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Changes&lt;/span&gt;&lt;span class="sh"&gt;"&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;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CREATE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ResourceRecordSet&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;subdomain&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;domain_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TTL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ResourceRecords&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_SERVER_PUBLIC_IP&lt;/span&gt;&lt;span class="sh"&gt;"&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;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This automatically registers a &lt;strong&gt;new subdomain&lt;/strong&gt; inside Route 53.&lt;/p&gt;


&lt;h2&gt;
  
  
  🛢️ Step 2 — Create Tenant Databases Dynamically
&lt;/h2&gt;

&lt;p&gt;Each tenant gets a &lt;strong&gt;separate MySQL database&lt;/strong&gt; for better isolation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mysql.connector&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_tenant_db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenant_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;root&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CREATE DATABASE IF NOT EXISTS &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tenant_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;_db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⚡ Step 3 — FastAPI Endpoint for Tenant Signup
&lt;/h2&gt;

&lt;p&gt;Whenever someone registers, we:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create their database&lt;/li&gt;
&lt;li&gt;Create their subdomain&lt;/li&gt;
&lt;li&gt;Return their live URL 🚀
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/register&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register_tenant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;create_tenant_db&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;create_subdomain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;subdomain&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tenant&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🌍 Step 4 — Configure NGINX for Subdomains
&lt;/h2&gt;

&lt;p&gt;We want all subdomains like &lt;code&gt;client1.example.com&lt;/code&gt; or &lt;code&gt;client2.example.com&lt;/code&gt; to hit the same backend, but serve different data.&lt;/p&gt;

&lt;p&gt;Add this to your &lt;strong&gt;Nginx config&lt;/strong&gt;:&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;server_name&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;^(?&amp;lt;subdomain&amp;gt;.+)\.example\.com$;&lt;/span&gt;

    &lt;span class="s"&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://127.0.0.1:8000&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_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&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;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&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;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&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;Then reload NGINX:&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;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🏗️ Final Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Signup → FastAPI → Route 53 → NGINX → Tenant DB → Tenant-Specific App&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Automatic subdomain creation
&lt;/li&gt;
&lt;li&gt;✅ Automatic database provisioning
&lt;/li&gt;
&lt;li&gt;✅ Scalable multi-tenant architecture
&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;With &lt;strong&gt;AWS Route 53&lt;/strong&gt;, &lt;strong&gt;FastAPI&lt;/strong&gt;, &lt;strong&gt;MySQL&lt;/strong&gt;, and &lt;strong&gt;NGINX&lt;/strong&gt;, we built a &lt;strong&gt;SaaS-ready system&lt;/strong&gt; that provisions &lt;strong&gt;subdomains and databases automatically&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;This approach works great for &lt;strong&gt;multi-tenant apps&lt;/strong&gt;, &lt;strong&gt;platforms&lt;/strong&gt;, and &lt;strong&gt;SaaS dashboards&lt;/strong&gt; — and saves hours of manual setup.&lt;/p&gt;




&lt;p&gt;💡 &lt;strong&gt;Pro Tip:&lt;/strong&gt; You can even take it further and integrate &lt;strong&gt;AWS ACM&lt;/strong&gt; for &lt;strong&gt;automatic SSL certificates&lt;/strong&gt; per subdomain.&lt;/p&gt;

&lt;p&gt;What do you think? Would you like me to share a guide on &lt;strong&gt;auto SSL + HTTPS for all subdomains&lt;/strong&gt; next? 🔐  &lt;/p&gt;

</description>
      <category>aws</category>
      <category>python</category>
      <category>saas</category>
      <category>devops</category>
    </item>
    <item>
      <title>Remote Access: A Complete Beginner-to-Advanced Guide</title>
      <dc:creator>Zed</dc:creator>
      <pubDate>Sun, 10 Aug 2025 20:26:05 +0000</pubDate>
      <link>https://forem.com/zedai00/remote-access-a-complete-beginner-to-advanced-guide-18ca</link>
      <guid>https://forem.com/zedai00/remote-access-a-complete-beginner-to-advanced-guide-18ca</guid>
      <description>&lt;p&gt;Remote access lets you control devices, transfer files, and manage servers without physically touching them. Whether you’re working from home, managing a personal server, or simply transferring files between devices, the tools and methods below cover secure, practical solutions for all platforms.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. What is Remote Access?
&lt;/h2&gt;

&lt;p&gt;Remote access is the ability to connect to another device over a network or the internet. Common uses include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;File transfer&lt;/strong&gt; between devices&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remote server management&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collaborating&lt;/strong&gt; with others on a shared machine&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring systems&lt;/strong&gt; without being on-site&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Protocols &amp;amp; Tools Overview
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Secure?&lt;/th&gt;
&lt;th&gt;Common Tools&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FTP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Transfer files&lt;/td&gt;
&lt;td&gt;❌ No (unencrypted)&lt;/td&gt;
&lt;td&gt;FileZilla, BusyBox FTP, Windows IIS FTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SFTP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Transfer files over SSH&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;OpenSSH, WinSCP, Cyberduck&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSH&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Secure terminal access&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;OpenSSH, PuTTY, Termius&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RDP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Remote desktop access&lt;/td&gt;
&lt;td&gt;✅ (with encryption)&lt;/td&gt;
&lt;td&gt;Windows RDP, Remmina, AnyDesk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mosh&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Persistent shell access&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;Mosh CLI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rsync&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sync files efficiently&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;rsync (Linux/macOS), cwRsync (Windows)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  3. SSH (Secure Shell) – Universal Secure Remote Access
&lt;/h2&gt;

&lt;p&gt;SSH is the most widely used method for remote server management.&lt;br&gt;&lt;br&gt;
It works on Linux, macOS, Windows (via PowerShell or PuTTY), and Android (via Termux).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On Linux/macOS:&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;ssh user@hostname_or_ip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Custom port:&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;-p&lt;/span&gt; 8022 user@hostname_or_ip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;On Windows:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install &lt;strong&gt;PuTTY&lt;/strong&gt; or use PowerShell:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="nx"&gt;hostname_or_ip&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Security Tip:&lt;/strong&gt; Use &lt;strong&gt;public key authentication&lt;/strong&gt; instead of passwords:&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; rsa &lt;span class="nt"&gt;-b&lt;/span&gt; 2048
ssh-copy-id user@hostname_or_ip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. SFTP – Secure File Transfer
&lt;/h2&gt;

&lt;p&gt;SFTP uses the SSH protocol to encrypt file transfers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Command line:&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;sftp user@hostname_or_ip
sftp &lt;span class="nt"&gt;-P&lt;/span&gt; 8022 user@hostname_or_ip  &lt;span class="c"&gt;# custom port&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Basic SFTP commands:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ls&lt;/code&gt; – list files&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;put file.txt&lt;/code&gt; – upload&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get file.txt&lt;/code&gt; – download&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GUI options:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WinSCP&lt;/strong&gt; (Windows)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cyberduck&lt;/strong&gt; (Windows/macOS)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FileZilla&lt;/strong&gt; (cross-platform)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Rsync – Sync Files Quickly
&lt;/h2&gt;

&lt;p&gt;Rsync transfers only changes, making it faster for repeated syncs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&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;rsync &lt;span class="nt"&gt;-av&lt;/span&gt; /local/path/ user@remote:/remote/path/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Over SSH with custom port:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rsync &lt;span class="nt"&gt;-av&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"ssh -p 8022"&lt;/span&gt; /local/path/ user@remote:/remote/path/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. Mosh – SSH for Unstable Connections
&lt;/h2&gt;

&lt;p&gt;Mosh is great if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Move between Wi-Fi and mobile data&lt;/li&gt;
&lt;li&gt;Have intermittent connectivity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install on both ends:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mosh user@hostname_or_ip
mosh &lt;span class="nt"&gt;--ssh&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ssh -p 8022"&lt;/span&gt; user@hostname_or_ip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  7. RDP (Remote Desktop Protocol)
&lt;/h2&gt;

&lt;p&gt;For full graphical desktop control.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Windows to Windows:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built-in &lt;strong&gt;Remote Desktop Connection&lt;/strong&gt; (&lt;code&gt;mstsc&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Linux/macOS to Windows:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;Remmina&lt;/strong&gt; (Linux) or &lt;strong&gt;Microsoft Remote Desktop&lt;/strong&gt; (macOS)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  8. Using Termux as a Remote Server
&lt;/h2&gt;

&lt;p&gt;If you want your &lt;strong&gt;Android device&lt;/strong&gt; to act as a server:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install Termux&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;openssh&lt;/code&gt; for SSH/SFTP&lt;/li&gt;
&lt;li&gt;Default SSH port: &lt;strong&gt;8022&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;pkg &lt;span class="nb"&gt;install &lt;/span&gt;openssh
sshd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Connect from PC:&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;-p&lt;/span&gt; 8022 192.168.x.x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Disable password login&lt;/strong&gt; once key-based authentication is set up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Change default ports&lt;/strong&gt; (e.g., 22 → 2222 or 8022).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use strong passwords&lt;/strong&gt; if keys aren’t possible.&lt;/li&gt;
&lt;li&gt;Keep your SSH/SFTP/RDP &lt;strong&gt;software updated&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Avoid exposing your server directly to the internet — use a VPN if possible.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  10. Quick Command Cheat Sheet
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# SSH login&lt;/span&gt;
ssh user@host
ssh &lt;span class="nt"&gt;-p&lt;/span&gt; PORT user@host

&lt;span class="c"&gt;# Generate SSH key&lt;/span&gt;
ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; rsa &lt;span class="nt"&gt;-b&lt;/span&gt; 2048

&lt;span class="c"&gt;# Copy SSH key to server&lt;/span&gt;
ssh-copy-id &lt;span class="nt"&gt;-p&lt;/span&gt; PORT user@host

&lt;span class="c"&gt;# SFTP file transfer&lt;/span&gt;
sftp user@host
put localfile
get remotefile

&lt;span class="c"&gt;# Rsync sync folder&lt;/span&gt;
rsync &lt;span class="nt"&gt;-av&lt;/span&gt; /local/path/ user@host:/remote/path/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;Remote access isn’t just for system administrators — it’s for anyone who wants seamless, secure control over their devices. Whether you’re syncing files from a laptop to a phone running Termux, managing a cloud server from your tablet, or helping a friend troubleshoot their computer remotely, mastering SSH, SFTP, and related tools will give you freedom and flexibility.&lt;/p&gt;

</description>
      <category>networking</category>
      <category>devops</category>
      <category>ssh</category>
      <category>remoteaccess</category>
    </item>
    <item>
      <title>How to Deploy Your App with Docker on Render in 5 Minutes</title>
      <dc:creator>Zed</dc:creator>
      <pubDate>Fri, 08 Aug 2025 18:08:01 +0000</pubDate>
      <link>https://forem.com/zedai00/how-to-deploy-your-app-with-docker-on-render-in-5-minutes-1fi3</link>
      <guid>https://forem.com/zedai00/how-to-deploy-your-app-with-docker-on-render-in-5-minutes-1fi3</guid>
      <description>&lt;p&gt;If you’ve built a full stack app and want a super easy way to get it online, &lt;strong&gt;Render + Docker&lt;/strong&gt; is a winning combo. In this quick guide, I’ll show you how I deployed my app using a Dockerfile on &lt;a href="https://render.com" rel="noopener noreferrer"&gt;Render.com&lt;/a&gt; — with zero server config!&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;A working app (Node.js, Python, etc.)&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;Dockerfile&lt;/code&gt; in your project root&lt;/li&gt;
&lt;li&gt;A GitHub repository&lt;/li&gt;
&lt;li&gt;A free account on &lt;a href="https://render.com" rel="noopener noreferrer"&gt;Render&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Create a Dockerfile
&lt;/h2&gt;

&lt;p&gt;Here’s an example &lt;code&gt;Dockerfile&lt;/code&gt; for a Node.js app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Use official Node image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18&lt;/span&gt;

&lt;span class="c"&gt;# Set working directory&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy all files&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Start the app&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm", "start"]&lt;/span&gt;

&lt;span class="c"&gt;# Expose port&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Python Flask, you might use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.11&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "app.py"]&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 5000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to customize the Dockerfile for your tech stack.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Push Your Code to GitHub
&lt;/h2&gt;

&lt;p&gt;If you haven’t already:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git init
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"initial commit"&lt;/span&gt;
git remote add origin &amp;lt;your-repo-url&amp;gt;
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Create a Web Service on Render
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://render.com" rel="noopener noreferrer"&gt;https://render.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;New + → Web Service&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Connect your GitHub repo&lt;/li&gt;
&lt;li&gt;Render will auto-detect your Dockerfile&lt;/li&gt;
&lt;li&gt;Set the following:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt;: Anything you like&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment&lt;/strong&gt;: Docker&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build Command&lt;/strong&gt;: Leave empty&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start Command&lt;/strong&gt;: Use from Dockerfile&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port&lt;/strong&gt;: &lt;code&gt;3000&lt;/code&gt; (or &lt;code&gt;5000&lt;/code&gt; for Flask)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create Web Service&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Render will start building your image and deploy it automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pro Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;.env&lt;/code&gt; file locally and set the same &lt;strong&gt;Environment Variables&lt;/strong&gt; in Render&lt;/li&gt;
&lt;li&gt;Add a &lt;code&gt;render.yaml&lt;/code&gt; file for Infrastructure as Code (optional)&lt;/li&gt;
&lt;li&gt;Set up &lt;strong&gt;automatic deploys&lt;/strong&gt; on every GitHub push&lt;/li&gt;
&lt;li&gt;Monitor logs directly in the Render dashboard&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Docker + Render makes it dead simple to go from local development to live deployment. No AWS console, no NGINX config — just push and deploy.&lt;/p&gt;

&lt;p&gt;If you found this helpful, leave a like, share with your dev friends, or drop questions in the comments!&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>webdev</category>
      <category>deployement</category>
    </item>
    <item>
      <title>Creating a Console Game In Java</title>
      <dc:creator>Zed</dc:creator>
      <pubDate>Sun, 04 Feb 2024 12:15:51 +0000</pubDate>
      <link>https://forem.com/zedai00/creating-a-console-game-in-java-3mfl</link>
      <guid>https://forem.com/zedai00/creating-a-console-game-in-java-3mfl</guid>
      <description>&lt;p&gt;Hello everyone, I have been learning Java for a while, and I was thinking about making a project to practice programming. After giving it some thought, I decided to make a Java snake game that runs on consoles.&lt;/p&gt;

&lt;p&gt;To create the game, I first had to figure out exactly where to move the cursor and print in the terminal. After some research, I found &lt;a href="https://en.wikipedia.org/wiki/ANSI_escape_code"&gt;ANSI Escape Sequences&lt;/a&gt;, which are incredibly powerful and simple-to-use escape sequences that can manipulate your terminal with just a few lines of code. Basically, you can print "ESC[{x};{y}H" to move the cursor to a specific x, y position, and it will move there. There are many commands of this type, such as those that manipulate color, change the background and foreground colors, sound an alarm, clear the screen, etc. I've found a good list of commands created by &lt;a href="https://github.com/fnky"&gt;fnky&lt;/a&gt;. You can see it here. (&lt;a href="https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797"&gt;https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;I attempted to use a for loop to move the cursor to random locations after fiddling with the ANSI codes, as you can see here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for (int i = 0; i&amp;lt;10; i++) {
   System.out.print("\e[i;iH");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above is merely an example; you can see that I am telling the terminal that the text that follows is an escape sequence by using the escape character "\e."&lt;/p&gt;

&lt;p&gt;The next big task was figuring out the width and height of the terminal so that the game's boundaries and play area could be defined. This was actually a pretty challenging task because every operating system has a different way of figuring out the size of the terminal, like:&lt;br&gt;
You may utilize tput in Linux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tput lines # get number of lines (Height)
tput cols # get number of columns (Width)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and for PowerShell on Windows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$Host.UI.RawUI.WindowSize.Width
$Host.UI.RawUI.WindowSize.Height
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, After researching, I've found multiple libraries, such as &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/jline/jline3"&gt;Jline&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mabe02/lanterna"&gt;Lanterna&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://picocli.info/"&gt;Picocli&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/fusesource/jansi"&gt;Jansi&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And after doing some research, I ultimately decided on JLine because it was neither too complicated, which would help me learn new things, nor too simple, which would have prevented me from creating the main game logic. As a result of my decision, I now know how to create a terminal and obtain its height and width, essentially.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Terminal terminal = TerminalBuilder.builder().system(true).dumb(false).build());
System.out.println(terminal.getWidth());
System.out.println(terminal.getHeight());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything is enclosed in a try block in order to utilize all of Jline's features and prevent errors from occurring when a terminal is created that is not supported. To enable full functionality for cross-platform apps, it requires an additional library (&lt;a href="https://github.com/jline/jline3?tab=readme-ov-file#jni-vs-jansi-vs-jna-vs-ffm-vs-exec"&gt;https://github.com/jline/jline3?tab=readme-ov-file#jni-vs-jansi-vs-jna-vs-ffm-vs-exec&lt;/a&gt;), which may be jansi or jna.&lt;/p&gt;

&lt;p&gt;After choosing and learning about JLine, I had to choose a build tool. Since I had never used Gradle before, I opted to use it so that I could become familiar with it. Next, I added JLine and Jna as dependencies.&lt;/p&gt;

&lt;p&gt;Finally, after all of this, it was time to actually create the game. To do this, I started by creating a game class that will manage all of the main game mechanisms and an input handler that will handle user input. The input handler was created in a new thread because it required user input to be entered in a nonblocking manner, meaning that while the game class updates the screen, the input handler will continue to receive user input in order to move the snake.&lt;/p&gt;

&lt;p&gt;I first created the bit class, which is the single bit of the snake, and the x and y coordinates of each bit are what will be used to print it. This is because the snake is composed of bits.&lt;/p&gt;

&lt;p&gt;Syncing the movement of two printed pieces together posed an extremely significant challenge. In order to make it appear as though it was moving, I first tried printing a single bit. To do this, I printed it while increasing its x or y coordinates by one, then I printed it while printing space in its previous coordinates. To achieve this, I added prevX and prevY to the Bit class. Next, I added a second bit by matching the current x and y coordinates with the previous x and y of the first bits. In this way, I only updated the first bit, and everything will move in that direction.&lt;/p&gt;

&lt;p&gt;In order to provide direction, I made an Enum Direction that will be mapped to the W A S D keys and contains the UP, DOWN, LEFT, and RIGHT directions.&lt;br&gt;
Thus, the enum value will change to UP, and the snake will move upward when a user clicks the W key in this manner. I have therefore devised a clever mechanism to move the snake by simply adding and subtracting the x and y so that&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UP: x--
Down: x++
Left: y--
Right: y++
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The snake will move in that direction if the aforementioned operations are carried out on it in x and y coordinates, so I added a switch case and ran the loop to account for this.&lt;/p&gt;

&lt;p&gt;In order to make the food appear, I made a new food class with x and y coordinates that were also randomly selected. This way, each time the snake eats the food, it will appear in a different location.&lt;/p&gt;

&lt;p&gt;In order to determine when the snake is eating the food, I added checks to the snake class by observing the xy of the snake's initial bite and the xy of the food. The food will reappear at a random location if both are the same.&lt;/p&gt;

&lt;p&gt;I also added a border check, noting that it was necessary in case the snake crossed it, so the game ends when the snake touches the border.&lt;/p&gt;

&lt;p&gt;and also when it touched its body, but I decided to leave that feature in place since I thought it was cool that a snake could move in any direction even when it touched its body.&lt;/p&gt;

&lt;p&gt;Thus, the main logic of the game was finally implemented. Colors and design were all that were lacking. After doing some research, I discovered that the jline ansi library or the ansi escape sequence can be used to implement colors. As a result, I chose to use the jline ansi library, and colors are now randomly selected from a color array. Additionally, I learned about ASCII art, or text-based art. After that, I came upon &lt;a href="https://asciiart.eu"&gt;Ascii Art&lt;/a&gt;, a website that allows you to manipulate any text using ASCII art. I decided to use it to create my game's logo, Start Menu, Pause Menu, and Game Over Menu.&lt;/p&gt;

&lt;p&gt;To make the snake appear like a rectangular box, I just colored a blank space a different color. For the food, I used various Unicode character sheet symbols, such as circles and zeros.&lt;a href="https://en.wikipedia.org/wiki/List_of_Unicode_characters"&gt;Unicode Character Sheet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And so, I have finally finished the game after much modification and refactoring of all the functions in various classes.&lt;br&gt;
So you can see the game's code in my Github page &lt;br&gt;
Repo:- &lt;a href="https://github.com/zedai00/snakez"&gt;https://github.com/zedai00/snakez&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was my experience making the snake game, and although I apologize for not going into too much detail, I will do so in the comments section if you have any. Thanks.&lt;/p&gt;

</description>
      <category>java</category>
      <category>gamedev</category>
      <category>terminal</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
