<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Linux &#8211; Other Things</title>
	<atom:link href="https://blog.adamzolo.com/category/linux/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.adamzolo.com</link>
	<description>Blog about Things by Adam Zolotarev</description>
	<lastBuildDate>Mon, 26 Jan 2026 16:10:56 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.1</generator>
	<item>
		<title>How We Fixed the &#8220;First Web Container is Unhealthy&#8221; Error: A DNS Deep Dive</title>
		<link>https://blog.adamzolo.com/how-we-fixed-the-first-web-container-is-unhealthy-error-a-dns-deep-dive/</link>
					<comments>https://blog.adamzolo.com/how-we-fixed-the-first-web-container-is-unhealthy-error-a-dns-deep-dive/#respond</comments>
		
		<dc:creator><![CDATA[Adam Zolo]]></dc:creator>
		<pubDate>Mon, 26 Jan 2026 16:10:56 +0000</pubDate>
				<category><![CDATA[docker]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://blog.adamzolo.com/?p=1082</guid>

					<description><![CDATA[The Error That Nearly Broke Our Deployment Three hours into our Kamal deployment, we were stuck in a loop: ERROR Failed to boot web on {ip_address} INFO First web container is unhealthy on {ip_address}, not booting any other roles The container would start, but Kamal&#8217;s health check kept failing. After 30 seconds, Kamal would kill&#8230;<p><a class="more-link" href="https://blog.adamzolo.com/how-we-fixed-the-first-web-container-is-unhealthy-error-a-dns-deep-dive/" title="Continue reading &#8216;How We Fixed the &#8220;First Web Container is Unhealthy&#8221; Error: A DNS Deep Dive&#8217;">Continue reading <span class="meta-nav">&#8594;</span></a></p>]]></description>
										<content:encoded><![CDATA[<br />
<h2>The Error That Nearly Broke Our Deployment</h2>
<p>Three hours into our Kamal deployment, we were stuck in a loop:</p>
<pre><code>ERROR Failed to boot web on {ip_address}
  INFO First web container is unhealthy on {ip_address}, not booting any other roles</code></pre>
<p>The container would start, but Kamal&#8217;s health check kept failing. After 30 seconds, Kamal would kill the container<br />
   and retry, creating an endless loop.</p>
<p>We spent hours debugging deployment scripts, PostgreSQL configurations, and Rails settings. The fix turned out to<br />
  be much simpler: DNS configuration.</p>
<h2>The Root Cause: Broken DNS Resolution</h2>
<h3>What Was Happening</h3>
<p>When Kamal tried to verify container health, it performed this sequence:</p>
<ol>
<li>Container starts → my_app-web-abc123 boots</li>
<li>Traefik (Kamal proxy) tries to check /up endpoint</li>
<li>DNS lookup → Resolve my_app-web-abc123 to an IP address</li>
<li>Health check fails → DNS resolution times out or fails</li>
<li>Container killed → Kamal marks it as unhealthy</li>
</ol>
<h3>The DNS Failure</h3>
<p>The Traefik container&#8217;s /etc/resolv.conf showed:</p>
<pre><code>nameserver 127.0.0.53
  search members.linode.com
  options edns0 trust-ad ndots:0</code></pre>
<p><strong>Problem:</strong> 127.0.0.53 is the host&#8217;s systemd-resolved DNS server. It&#8217;s not accessible from inside<br />
  Docker containers!</p>
<p>When Traefik tried to resolve my_app-web-abc123:</p>
<ul>
<li>It queried 127.0.0.53 (systemd-resolved)</li>
<li>The query failed with &#8220;connection refused&#8221;</li>
<li>Health check failed</li>
<li>Container was killed</li>
</ul>
<h2>The Solution: Proper Docker DNS Configuration</h2>
<h3>What We Fixed</h3>
<p>We configured Docker&#8217;s DNS settings in <code>/etc/docker/daemon.json</code>:</p>
<pre><code>{
    "dns": ["127.0.0.11", "8.8.8.8", "1.1.1.1"]
  }</code></pre>
<h3>Why This Works</h3>
<p><strong>1. 127.0.0.11 (Docker&#8217;s Internal DNS) &#8211; First Priority</strong></p>
<ul>
<li>Resolves container hostnames automatically</li>
<li>Handles inter-container communication</li>
<li>Always available inside Docker networks</li>
</ul>
<p><strong>2. 8.8.8.8 (Google DNS) &#8211; Second Priority</strong></p>
<ul>
<li>Resolves external domains (APIs, gems, etc.)</li>
<li>Fast and reliable</li>
<li>Global infrastructure</li>
</ul>
<p><strong>3. 1.1.1.1 (Cloudflare DNS) &#8211; Third Priority</strong></p>
<ul>
<li>Privacy-focused external DNS</li>
<li>Backup if 8.8.8.8 fails</li>
<li>No query logging</li>
</ul>
<h3>How Docker Uses This</h3>
<p>Docker&#8217;s DNS resolution order:</p>
<ol>
<li>Try 127.0.0.11 (internal) → container names</li>
<li>If that fails → 8.8.8.8 (external) → domains</li>
<li>If that fails → 1.1.1.1 (external) → domains</li>
</ol>
<h2>The IPv4/IPv6 Issue</h2>
<p>While debugging, we discovered another subtle problem:</p>
<h3>The IPv6 Trap</h3>
<p>The server setup script used:</p>
<pre><code>SERVER_IP=$(curl -s ifconfig.me || echo "ip_address_goes_here")</code></pre>
<p><strong>Problem:</strong> ifconfig.me returned an IPv6 address:</p>
<pre><code>2600:3c03::...</code></pre>
<p>This IPv6 address was used in PostgreSQL&#8217;s pg_hba.conf:</p>
<pre><code>host my_app_production my_app_user 2600:3c03.../32 md5</code></pre>
<p>PostgreSQL had issues with this IPv6 address, causing authentication failures.</p>
<h3>The Fix</h3>
<p>Force IPv4 detection:</p>
<pre><code>SERVER_IP=$(curl -s -4 ifconfig.me || echo "ip_address_goes_here")</code></pre>
<p>The <code>-4</code> flag ensures we always get an IPv4 address, which PostgreSQL handles reliably.</p>
<h2>The PostgreSQL Network Isolation Issue</h2>
<h3>The Problem</h3>
<p>Kamal uses a separate Docker network (172.18.0.0/16) for containers, while PostgreSQL is on the host&#8217;s Docker<br />
  bridge network (172.17.0.0/16).</p>
<p>The firewall only allowed 172.17.0.0/16:</p>
<pre><code>5432/tcp  ALLOW  172.17.0.0/16</code></pre>
<h3>The Fix</h3>
<p>Add the Kamal network to both firewall and PostgreSQL config:</p>
<p><strong>Firewall (ufw):</strong></p>
<pre><code>sudo ufw allow from 172.18.0.0/16 to any port 5432</code></pre>
<p><strong>PostgreSQL (pg_hba.conf):</strong></p>
<pre><code>host my_app_production my_app_user 172.18.0.0/16 md5</code></pre>
<h2>Complete Fix in our setup script</h2>
<h3>IPv4 Fix </h3>
<pre><code>SERVER_IP=$(curl -s -4 ifconfig.me || echo "ip_address_goes_here")</code></pre>
<h3>Kamal Network Firewall Rule</h3>
<pre><code>sudo ufw allow from 172.18.0.0/16 to any port 5432</code></pre>
<h3>PostgreSQL Kamal Network Rule </h3>
<pre><code>host $DB_NAME $DB_USER 172.18.0.0/16 md5</code></pre>
<h3>Docker DNS Configuration </h3>
<pre><code>{
    "dns": ["127.0.0.11", "8.8.8.8", "1.1.1.1"]
  }</code></pre>
<h2>Key Takeaways</h2>
<ol>
<li><strong>DNS is Critical for Container Orchestration</strong>
<ul>
<li>Always configure Docker&#8217;s DNS properly</li>
<li>Include both internal and external DNS servers</li>
<li>Test DNS resolution from containers</li>
</ul>
</li>
<li><strong>Network Isolation Matters</strong>
<ul>
<li>Docker networks are isolated by default</li>
<li>PostgreSQL must allow connections from all Docker networks</li>
<li>Firewall rules must match</li>
</ul>
</li>
<li><strong>IPv4 vs IPv6 Can Break Things</strong>
<ul>
<li>PostgreSQL works better with IPv4</li>
<li>Force IPv4 when detecting server IPs</li>
<li>Test both IPv4 and IPv6 connectivity</li>
</ul>
</li>
<li><strong>Health Checks are Essential</strong>
<ul>
<li>The /up endpoint is critical for Kamal</li>
<li>DNS must work for health checks to succeed</li>
<li>Timeout settings matter (30s default)</li>
</ul>
</li>
</ol>
<h2>Troubleshooting DNS Issues</h2>
<p>If you encounter &#8220;First web container is unhealthy&#8221;:</p>
<ol>
<li><strong>Check Container Logs</strong><br />
  <code>docker logs my_app-web-abc123</code></li>
<li><strong>Check Traefik/Kamal Proxy Logs</strong><br />
  <code>docker logs kamal-proxy | grep -i healthcheck</code></li>
<li><strong>Test DNS Resolution</strong>
<pre><code># From inside Traefik container
  docker exec kamal-proxy getent hosts my_app-web-abc123
  docker exec kamal-proxy getent hosts google.com</code></pre>
</li>
<li><strong>Verify DNS Configuration</strong>
<pre><code># Check daemon.json
  cat /etc/docker/daemon.json

  # Check container's resolv.conf
  docker exec kamal-proxy cat /etc/resolv.conf</code></pre>
</li>
<li><strong>Check PostgreSQL Connectivity</strong>
<pre><code># From kamal network
  docker run --rm --network kamal postgres:16 psql \
    -h 172.17.0.1 -U my_app_user -d my_app_production -c "SELECT 1"</code></pre>
</li>
</ol>
<h2>Results</h2>
<p>After implementing all fixes:</p>
<ul>
<li><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> DNS resolution works (internal and external)</li>
<li><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Health checks pass (Traefik can reach containers)</li>
<li><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> PostgreSQL connections work (from both Docker networks)</li>
<li><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Deployments succeed (consistent, reliable)</li>
<li><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> IPv4 detection works (no IPv6 issues)</li>
</ul>
<h2>Final Thoughts</h2>
<p>The &#8220;First web container is unhealthy&#8221; error can be a DNS configuration issue, not a deployment or application<br />
  problem.</p>
<p>By understanding how Docker networks work, how DNS resolution functions, and how PostgreSQL authentication works, we can prevent this issue from ever occurring again.</p>
<p><strong>Key files to review:</strong></p>
<ul>
<li><code>/etc/docker/daemon.json</code> &#8211; Docker DNS configuration</li>
<li><code>/etc/postgresql/16/main/pg_hba.conf</code> &#8211; PostgreSQL authentication</li>
<li><code>/etc/ufw/rules.conf</code> &#8211; Firewall rules</li>
</ul>
<p>The fix is now automated in our setup script, ensuring new servers have proper DNS and network configuration from<br />
  day one.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.adamzolo.com/how-we-fixed-the-first-web-container-is-unhealthy-error-a-dns-deep-dive/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Setting up a free Minecraft Server on Oracle Cloud</title>
		<link>https://blog.adamzolo.com/setting-up-a-free-minecraft-server-on-oracle-cloud/</link>
					<comments>https://blog.adamzolo.com/setting-up-a-free-minecraft-server-on-oracle-cloud/#respond</comments>
		
		<dc:creator><![CDATA[Adam Zolo]]></dc:creator>
		<pubDate>Sun, 26 Jun 2022 15:52:09 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Minecraft]]></category>
		<guid isPermaLink="false">https://blog.adamzolo.com/?p=1019</guid>

					<description><![CDATA[Update 2 months later Somewhat unsurprisingly, given Oracle reputation, they disabled my supposedly always free resources two months later. Turns out, they disable the resources created while you were on trial. However, you can still make a clone of your boot volume, then terminate your previous instance, then recreate your instance from the cloned volume.&#8230;<p><a class="more-link" href="https://blog.adamzolo.com/setting-up-a-free-minecraft-server-on-oracle-cloud/" title="Continue reading &#8216;Setting up a free Minecraft Server on Oracle Cloud&#8217;">Continue reading <span class="meta-nav">&#8594;</span></a></p>]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">Update 2 months later</h2>



<p>Somewhat unsurprisingly, given Oracle reputation, they disabled my supposedly always free resources two months later. Turns out, they disable the resources created while you were on trial. However, you can still make a clone of your boot volume, then terminate your previous instance, then recreate your instance from the cloned volume. It works so far.</p>



<p></p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>In this tutorial, we&#8217;ll set up a free Minecraft server using a free tier Oracle Cloud.</p>



<h2 class="wp-block-heading">Setting up an Oracle Free Tier</h2>



<p><a rel="noreferrer noopener" href="https://www.oracle.com/cloud/sign-in.html" target="_blank">Signup for Oracle free account</a>. Since this is Oracle, you probably want to check on the billing from time to time to make sure they didn&#8217;t start your changing for what they promised to be free.</p>



<p>Create a new compute instance. For max compatibility, we&#8217;ll use Ubuntu 18.04. You can try your luck with a newer version.</p>



<p> For the shape, select Ampere with 4 OCPU and 6GB Ram. This should still qualify for the free tier and should still be plenty for a Minecraft server.</p>



<p>Add your public SSH keys in the &#8220;Add SSH keys&#8221; section.</p>



<p>Create the instance. </p>



<p>After it&#8217;s created, go to the instance details, find &#8220;Primary VNIC&#8221; section, and open the subnet link (or create a new one).</p>



<p>Open Default Security List (or create a new one if one doesn&#8217;t exist yet)</p>



<p>Add Ingress Rules to open TCP/UDP ports 19132 for Bedrock and 25565 for Java edition (or both). Use CIDR for Source Type, 0.0.0.0/0 for Source CIDR, 19132 for Destination port. Repeat for TCP. Repeat for 25565 if planning to use Java edition.</p>



<p></p>



<p>SSH to your server</p>



<p>Upgrade all packages:</p>



<pre class="wp-block-code has-white-background-color has-background"><code>sudo apt-get update
sudo apt-get upgrade</code></pre>



<p>Let&#8217;s reset the firewall rules and open the ssh and Minecraft ports:</p>



<pre class="wp-block-code"><code>sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -F
sudo iptables-save
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 19132/udp
sudo ufw allow 19132/tcp
sudo ufw allow 25565/udp
sudo ufw allow 25565/tcp
sudo ufw enable
sudo ufw status</code></pre>



<p>Optionally install zsh and vim:</p>



<pre class="wp-block-code"><code>sudo apt install zsh<br>sudo apt install vim</code></pre>



<h2 class="wp-block-heading">Bedrock</h2>



<p>SSH to your server: <code>ssh ubuntu@ip_address</code></p>



<p>We&#8217;ll use this script to setup the server: <a href="https://github.com/TheRemote/MinecraftBedrockServer">https://github.com/TheRemote/MinecraftBedrockServer</a></p>



<pre class="wp-block-code"><code>curl https://raw.githubusercontent.com/TheRemote/MinecraftBedrockServer/master/SetupMinecraft.sh | bash</code></pre>



<p>Use the following commands to start/restart the service (you specified the service name when you ran the installation script):</p>



<p></p>



<p>sudo systemctl stop minecraft_service<br>sudo systemctl start minecraft_service<br>sudo systemctl restart minecraft_service</p>



<pre class="wp-block-code"><code>screen -R
# To disconnect (do not do ctrl-c or it may kill the minecraft service):
ctrl-A-D</code></pre>



<h2 class="wp-block-heading">Java Edition</h2>



<p>This section is only for Java edition. Don&#8217;t use it if you need the Bedrock version.</p>



<p>We&#8217;ll use this script to install the Java edition Paper server: <a rel="noreferrer noopener" href="https://github.com/TheRemote/RaspberryPiMinecraft" target="_blank">https://github.com/TheRemote/RaspberryPiMinecraft</a>.</p>



<p>SSH to your server and run <code>curl https://raw.githubusercontent.com/TheRemote/RaspberryPiMinecraft/master/SetupMinecraft.sh | bash</code>. All default settings should be fine.</p>



<p></p>



<p>If you want Bedrock users to use your server, we can install Geyser plugin:</p>



<p>We&#8217;ll download the latest Geyser-Spigot server from <a href="https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/">https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/</a>. </p>



<p>On the server run:</p>



<pre class="wp-block-code"><code>cd minecraft/plugins/
curl -O https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/master/lastSuccessfulBuild/artifact/bootstrap/spigot/target/Geyser-Spigot.jar</code></pre>



<p>If you also want Bedrock users to be able to login with their Microsoft account without requiring a separate Java account, we can also install a floodgate plugin:</p>



<pre class="wp-block-code"><code>curl -O https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/lastSuccessfulBuild/artifact/spigot/build/libs/floodgate-spigot.jar

Restart the Minecraft server with <code>sudo systemctl restart minecraft.service</code></code></pre>



<h2 class="wp-block-heading">DNS Records with Cloudflare</h2>



<p>Add an A record for your server IP. For example, if you own example.com and want to connect to your server using mct.example.com, then add an A record for mct pointing to your server IP.</p>



<p>Add 2 srv records for each port (19132 and 25565):</p>



<p>Use <code>mct</code> for Name, <code>_minecraft</code> for service, 0 for both Priority and Weight, UDP for protocol, <code>mct.example.com</code> for the target. <code>mct</code> is just the subdomain that you can change to whatever you want. Repeat for TCP and then for each port (19132 and 25565)</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.adamzolo.com/setting-up-a-free-minecraft-server-on-oracle-cloud/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Checking/Cleaning Disk Space on Linux</title>
		<link>https://blog.adamzolo.com/linux-disk-space-cleaning/</link>
					<comments>https://blog.adamzolo.com/linux-disk-space-cleaning/#respond</comments>
		
		<dc:creator><![CDATA[Adam Zolo]]></dc:creator>
		<pubDate>Thu, 05 Jul 2018 01:08:24 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<guid isPermaLink="false">http://blog.adamzolo.com/?p=762</guid>

					<description><![CDATA[Check the disk space (may need to install ncdu first): Clean up unused stuff: clean: clean clears out the local repository of retrieved package files. It removes everything but the lock file from /var/cache/apt/archives/ and /var/cache/apt/archives/partial/. When APT is used as a dselect(1) method, clean is run automatically. Those who do not use dselect will&#8230;<p><a class="more-link" href="https://blog.adamzolo.com/linux-disk-space-cleaning/" title="Continue reading &#8216;Checking/Cleaning Disk Space on Linux&#8217;">Continue reading <span class="meta-nav">&#8594;</span></a></p>]]></description>
										<content:encoded><![CDATA[<p>Check the disk space (may need to install ncdu first):</p>
<pre class="brush: plain; title: ; notranslate">
sudo ncdu /
</pre>
<p>Clean up unused stuff:</p>
<pre class="brush: plain; title: ; notranslate">
sudo apt-get clean
sudo apt-get autoclean
sudo apt-get autoremove
</pre>
<p><strong>clean</strong>: clean clears out the local repository of retrieved package files. It removes everything but the lock file from /var/cache/apt/archives/ and /var/cache/apt/archives/partial/. When APT is used as a dselect(1) method, clean is run automatically. Those who do not use dselect will likely want to run apt-get clean from time to time to free up disk space.</p>
<p><strong>autoclean</strong>: Like clean, autoclean clears out the local repository of retrieved package files. The difference is that it only removes package files that can no longer be downloaded, and are largely useless. This allows a cache to be maintained over a long period without it growing out of control. The configuration option APT::Clean-Installed will prevent installed packages from being erased if it is set to off.</p>
<p><strong>autoremove</strong>: is used to remove packages that were automatically installed to satisfy dependencies for some package and that are no more needed.</p>
<p>See a related question on askubuntu: <a href="https://askubuntu.com/questions/3167/what-is-difference-between-the-options-autoclean-autoremove-and-clean" rel="noopener" target="_blank">https://askubuntu.com/questions/3167/what-is-difference-between-the-options-autoclean-autoremove-and-clean</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.adamzolo.com/linux-disk-space-cleaning/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>NGINX Subdomains</title>
		<link>https://blog.adamzolo.com/nginx-subdomains/</link>
					<comments>https://blog.adamzolo.com/nginx-subdomains/#respond</comments>
		
		<dc:creator><![CDATA[Adam Zolo]]></dc:creator>
		<pubDate>Sat, 21 Oct 2017 15:25:02 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<guid isPermaLink="false">http://blog.adamzolo.com/?p=683</guid>

					<description><![CDATA[In the example below we serve index.html from /var/www/html/your_site directory when accessing &#8220;domain.com&#8221;; and we send all requests to local application server running locally on port 4000 when accessing subdomain.domain.com File: /etc/nginx/sites-enabled/default server { listen 80; listen &#x5B;::]:80; server_name domain.com; location / { root /var/www/html/your_site; try_files $uri /index.html; } } server { listen 80; listen&#8230;<p><a class="more-link" href="https://blog.adamzolo.com/nginx-subdomains/" title="Continue reading &#8216;NGINX Subdomains&#8217;">Continue reading <span class="meta-nav">&#8594;</span></a></p>]]></description>
										<content:encoded><![CDATA[<p>In the example below we serve index.html from /var/www/html/your_site directory when accessing &#8220;domain.com&#8221;; and we send all requests to local application server running locally on port 4000 when accessing subdomain.domain.com</p>
<p>File: /etc/nginx/sites-enabled/default</p>
<pre class="brush: plain; title: ; notranslate">

server {
        listen 80;
        listen &#x5B;::]:80;

        server_name domain.com;

        location / {
                root /var/www/html/your_site;
                try_files $uri /index.html;
        }
}

server {
  listen 80;
  listen &#x5B;::]:80;

  server_name subdomain.domain.com;

  location / {
     proxy_redirect off;
     proxy_pass http://127.0.0.1:4000/;
  }
}
</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.adamzolo.com/nginx-subdomains/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Deploying React to Linux Server with Git Push</title>
		<link>https://blog.adamzolo.com/deploy-react-to-linux-server-git-deploy/</link>
					<comments>https://blog.adamzolo.com/deploy-react-to-linux-server-git-deploy/#respond</comments>
		
		<dc:creator><![CDATA[Adam Zolo]]></dc:creator>
		<pubDate>Wed, 06 Sep 2017 01:37:39 +0000</pubDate>
				<category><![CDATA[git]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[React]]></category>
		<guid isPermaLink="false">http://blog.adamzolo.com/?p=663</guid>

					<description><![CDATA[Selected VPS: Linode, 1GB Ram, 20 GB SSD, 1 TB transfer OS: Ubuntu 17.04 Web Server: Ngnix If you’d like to try Linode, I would greatly appreciate using this referral link – Linode: SSD Cloud Hosting &#038; Linux Servers Start with regular updates apt-get update &#38;&#38; apt-get upgrade Set up fail2ban and Firewall I&#8217;m installing&#8230;<p><a class="more-link" href="https://blog.adamzolo.com/deploy-react-to-linux-server-git-deploy/" title="Continue reading &#8216;Deploying React to Linux Server with Git Push&#8217;">Continue reading <span class="meta-nav">&#8594;</span></a></p>]]></description>
										<content:encoded><![CDATA[<p>Selected VPS: Linode, 1GB Ram, 20 GB SSD, 1 TB transfer<br />
OS: Ubuntu 17.04<br />
Web Server: Ngnix<br />
If you’d like to try Linode, I would greatly appreciate using this referral link – <a href="https://www.linode.com/?r=1e7f26b4b71e72c400ad072b526b5b846cceae1e" target="_blank">Linode: SSD Cloud Hosting &#038; Linux Servers</a></p>
<p>Start with regular updates</p>
<pre class="brush: plain; title: ; notranslate">
apt-get update &amp;&amp; apt-get upgrade
</pre>
<h3>Set up fail2ban and Firewall</h3>
<p>I&#8217;m installing fail2ban 0.10 since it supports ipv6. At the time of this post, it is not available as a regular package.</p>
<pre class="brush: plain; title: ; notranslate">
wget https://github.com/fail2ban/fail2ban/archive/0.10.0.tar.gz
tar -xvzf 0.10.0.tar.gz
python3 setup.py install

#To enable fail2ban as an automatic service, copy the script for your distro from the files directory to /etc/init.d.

cp files/debian-initd /etc/init.d/fail2ban
update-rc.d fail2ban defaults
service fail2ban start

#Add local jail
awk '{ printf &quot;# &quot;; print; }' /etc/fail2ban/jail.conf | sudo tee /etc/fail2ban/jail.local
vim /etc/fail2ban/jail.local
</pre>
<p>uncomment sshd section and add<br />
enabled = true</p>
<pre class="brush: plain; title: ; notranslate">
sudo apt-get install sendmail iptables-persistent
sudo service fail2ban start
</pre>
<p><strong>Firewall </strong>( allow established connections, traffic generated by the server itself, traffic destined for our SSH and web server ports. We will drop all other traffic):</p>
<pre class="brush: plain; title: ; notranslate">
sudo service fail2ban stop
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
sudo iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
sudo iptables -A INPUT -j DROP

# easy way to rate-limit ssh with ufw:
# technically, we could do all of the iptables stuff with ufw
ufw enable
ufw limit ssh
</pre>
<p><strong>If using IPv6:</strong></p>
<pre class="brush: plain; title: ; notranslate">
ip6tables -A INPUT -p tcp --dport 80 -j ACCEPT
ip6tables -A INPUT -p tcp --dport 443 -j ACCEPT
ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT # (replace with your undisclosed port)
ip6tables -A INPUT -p icmpv6 -j ACCEPT
ip6tables -A INPUT -j REJECT
ip6tables -A FORWARD -j REJECT
</pre>
<p>View iptables rules:</p>
<pre class="brush: plain; title: ; notranslate">
sudo iptables -S
</pre>
<p>Save iptables rules:</p>
<pre class="brush: plain; title: ; notranslate">
sudo dpkg-reconfigure iptables-persistent
sudo service fail2ban start
</pre>
<h3>SSH</h3>
<pre class="brush: plain; title: ; notranslate">
vim /etc/ssh/sshd_config

#Add or uncomment (if using Ubuntu &lt; 17.04)
protocol 2

#Add allowed ciphers
Ciphers aes128-ctr,aes192-ctr,aes256-ctr
KexAlgorithms ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256
MACs hmac-sha2-256,hmac-sha2-512
</pre>
<p><strong>Restart and test ssh config:</strong></p>
<pre class="brush: plain; title: ; notranslate">
service sshd restart
#returns nothing if everything configured properly
sshd -t
</pre>
<h3>NGINX</h3>
<pre class="brush: plain; title: ; notranslate">
sudo apt-get install software-properties-common
sudo add-apt-repository ppa:nginx/stable
sudo apt-get install nginx
service nginx status
</pre>
<p>Update /etc/nginx/sites-enabled/default</p>
<pre class="brush: plain; title: ; notranslate">
root /var/www/html/your_site;

location / {
# Some comments...
try_files $uri /index.html;   # ADD THIS
}

sudo service nginx restart

</pre>
<h3>Installing React Dependencies</h3>
<pre class="brush: plain; title: ; notranslate">
# install yarn
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo &quot;deb https://dl.yarnpkg.com/debian/ stable main&quot; | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update &amp;&amp; sudo apt-get install yarn
 
#install node (apt-get repo has an older version of Node)
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs
sudo apt-get install -y build-essential

</pre>
<h3>GIT Push Deploy</h3>
<p>Let&#8217;s set up git on the server</p>
<pre class="brush: plain; title: ; notranslate">
apt-get install git-core
mkdir repos &amp;&amp; cd repos
mkdir your_site.git
cd your_site.git
git init --bare
</pre>
<p><b>Set up a post-push hook</b><br />
cd /repos/your_app.git/hooks<br />
touch post-receive</p>
<pre class="brush: plain; title: ; notranslate">
#!/bin/bash -l

GIT_REPO=$HOME/repos/your_app.git
TMP_GIT_CLONE=$HOME/tmp/git/your_app
PUBLIC_WWW=/var/www/html

git clone $GIT_REPO $TMP_GIT_CLONE
cd $TMP_GIT_CLONE
yarn install
yarn build
rm -rf $PUBLIC_WWW/your_app_bup
mv $PUBLIC_WWW/your_app $PUBLIC_WWW/your_app_bup
cp -a build/. $PUBLIC_WWW/your_app
rm -Rf $TMP_GIT_CLONE
exit
</pre>
<p>Run on post-receiv:</p>
<pre class="brush: plain; title: ; notranslate">
chmod +x post-receive
</pre>
<p>On your local machine:</p>
<pre class="brush: plain; title: ; notranslate">
git remote add linode root@remote_server_address:repos/your_app.git
git push linode master
</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.adamzolo.com/deploy-react-to-linux-server-git-deploy/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
