website/blog/2022-09-10-how-to-publish-website-without-librepages/index.html

839 lines
43 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/site.webmanifest" />
<link rel="me" href="https://gts.batsense.net.net/@librepages" />
<link rel="stylesheet" href="https://librepages.org/main.css" />
<link
rel="stylesheet"
media="screen and (max-width: 1300px)"
href="https://librepages.org/mobile.css"
/>
<meta name="referrer" content="no-referrer-when-downgrade" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="https://librepages.org/main.css" />
<link
rel="stylesheet"
media="screen and (max-width: 1300px)"
href="https://librepages.org/mobile.css"
/>
<meta name="referrer" content="no-referrer-when-downgrade" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>How to deploy a website WITHOUT LibrePages | LibrePages: JAMstack platform with focus on privacy and speed</title>
<meta name="referrer" content="no-referrer-when-downgrade" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Automation services like LibrePages exist to make lives easier but how do you do the same manually, on self-hosted hardware, or in the cloud?" />
<meta property="og:title" content="How to deploy a website WITHOUT LibrePages | LibrePages: JAMstack platform with focus on privacy and speed" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https:&#x2F;&#x2F;librepages.org" />
<meta property="og:description" content="Automation services like LibrePages exist to make lives easier but how do you do the same manually, on self-hosted hardware, or in the cloud?" />
<meta
property="og:site_name"
content="How to deploy a website WITHOUT LibrePages | LibrePages: JAMstack platform with focus on privacy and speed"
/>
<link
rel="apple-touch-icon"
sizes="57x57"
href="https://librepages.org/apple-icon-57x57.png?h=aa7556c6917e2715fc5cd91b0f71abf54c25fb3f4596b83938485bd339b3ee5c"
/>
<link
rel="apple-touch-icon"
sizes="60x60"
href="https://librepages.org/apple-icon-60x60.png?h=3c65021633e27b12573a4d95ee104960edeeb8448d016cc4a3a8c009956f455b"
/>
<link
rel="apple-touch-icon"
sizes="72x72"
href="https://librepages.org/apple-icon-72x72.png?h=7e6ea650d40b0c229eb8991d4bdaaeaf3a4fdc37b4c91c7e0f6705f4ccbd4823"
/>
<link
rel="apple-touch-icon"
sizes="76x76"
href="https://librepages.org/apple-icon-76x76.png?h=14cc3b66876cc79fe49f4bdf43cfa342dd12249fb32ebb4bf5895cac9fd2eaba"
/>
<link
rel="apple-touch-icon"
sizes="114x114"
href="https://librepages.org/apple-icon-114x114.png?h=a7e320f87a86aa0e037e78635c5f5042e02bf3adaf5c7a3163a108b004f1874e"
/>
<link
rel="apple-touch-icon"
sizes="120x120"
href="https://librepages.org/apple-icon-120x120.png?h=0555c76525ad4b8e974217be648c2691643b0ae09c1447bee571bdf51d324e5a"
/>
<link
rel="apple-touch-icon"
sizes="144x144"
href="https://librepages.org/apple-icon-144x144.png?h=3c6dcd632f3eca17cf7cc6153e9b372183518168754e2d8adb6bc549cfc89694"
/>
<link
rel="apple-touch-icon"
sizes="152x152"
href="https://librepages.org/apple-icon-152x152.png?h=0de6ee6daa86c4800faa71c0ba940a749b025c83f1150b19f7817bac9558344e"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="https://librepages.org/apple-icon-180x180.png?h=4015bdb0896669f24d0be4e93fc9625c771a746060906dd94ed07ed2b3a88ede"
/>
<link
rel="icon"
type="image/png"
sizes="192x192"
href="https://librepages.org/android-icon-192x192.png?h=4065738be7277800667ab5dab97c610d8b76f7c9d7835266ecf440a1336b179a"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="https://librepages.org/favicon-32x32.png?h=19f5fc89580c10a37da127a18cb6d18427f8604617fe3c1d163a5528c4832094"
/>
<link
rel="icon"
type="image/png"
sizes="96x96"
href="https://librepages.org/favicon-96x96.png?h=f1dbc55e44179d839832093c008b0bedea79c3b21b1af68adb6d70c3e21227f5"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="https://librepages.org/favicon-16x16.png?h=a7056d65f8aa73fbaf9e97dcd2e685ac67489a76c0b8e715936970b118d74700"
/>
<link
rel="manifest"
href="https://librepages.org/manifest.json?h=27eca3e8297eb7ff340deb3849b210185a459b3845456aa4d0036f6d966b3518"
/>
<meta name="msapplication-TileColor" content="#ffffff" />
<meta
name="msapplication-TileImage"
content="https://librepages.org/ms-icon-144x144.png?h=3c6dcd632f3eca17cf7cc6153e9b372183518168754e2d8adb6bc549cfc89694"
/>
<meta name="theme-color" content="#ffffff" />
</head>
<!-- Matomo -->
<script>
var _paq = (window._paq = window._paq || []);
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(["setCookieDomain", "*.librepages.org"]);
_paq.push(["trackPageView"]);
_paq.push(["enableLinkTracking"]);
(function () {
var u = "//matomo.librepages.org/";
_paq.push(["setTrackerUrl", u + "matomo.php"]);
_paq.push(["setSiteId", "3"]);
var d = document,
g = d.createElement("script"),
s = d.getElementsByTagName("script")[0];
g.async = true;
g.src = u + "matomo.js";
s.parentNode.insertBefore(g, s);
})();
</script>
<noscript
><p>
<img
src="//matomo.librepages.org/matomo.php?idsite=3&amp;rec=1"
style="border: 0"
alt=""
/></p
></noscript>
<!-- End Matomo Code -->
</head>
<body class="base">
<div id="check-icon" data-check="https://librepages.org/icons/check.svg?h=376636abc0ddb30b4748ab5d4416d64ec96862f6c4acf04ef3bf9a7f8f323238"></div>
<div id="clipboard-icon" data-clipboard="https://librepages.org/icons/clipboard.svg?h=202bedb305272a2e2a87ca77aae6e8c61403fc33fb8d8b3cda98df7226ddba84"></div>
<header><nav class="nav__container">
<input type="checkbox" class="nav__toggle" id="nav__toggle" />
<div class="nav__header">
<a class="nav__logo-container" href="/">
<p class="nav__home-btn">LibrePages</p>
</a>
<label class="nav__hamburger-menu" for="nav__toggle">
<span class="nav__hamburger-inner"></span>
</label>
</div>
<div class="nav__spacer--small"></div>
<div class="nav__link-group">
<div class="nav__link-container">
<a class="nav__link" rel="noreferrer" href="&#x2F;about&#x2F;">About</a>
</div>
<div class="nav__link-container">
<a class="nav__link" rel="noreferrer" href="&#x2F;blog&#x2F;">Blog</a>
</div>
<div class="nav__link-container">
<a class="nav__link" rel="noreferrer" href="https:&#x2F;&#x2F;matrix.to&#x2F;#&#x2F;#librepages:matrix.batsense.net">Chat</a>
</div>
<div class="nav__link-container">
<a class="nav__link" rel="noreferrer" href="https:&#x2F;&#x2F;docs.librepages.org">Docs</a>
</div>
<div class="nav__link-container">
<a class="nav__link" rel="noreferrer" href="https:&#x2F;&#x2F;gts.batsense.net&#x2F;@librepages">Fediverse</a>
</div>
<div class="nav__link-container">
<a class="nav__link" rel="noreferrer" href="https:&#x2F;&#x2F;git.batsense.net&#x2F;LibrePages">Source Code</a>
</div>
</div>
<div class="nav__spacer"></div>
<div class="nav__link-group--small">
<div class="nav__link-container">
<a class="nav__link" rel="noreferrer" href="https:&#x2F;&#x2F;demo.librepages.org&#x2F;">Demo</a>
</div>
</div>
</nav>
</header>
<!-- See ../sass/main.scss. Required for pushing footer to the very
bottom of the page -->
<div class="main__content-container">
<main>
<div class="page__container">
<h1 class="page__group-title">How to deploy a website WITHOUT LibrePages</h1>
<p class="blog__post-meta">
<a href="https:&#x2F;&#x2F;batsense.net" class="post__author">Aravinth Manivannan</a>
&middot; 10
September
,
2022 &middot; <b>9 min read</b>
</p>
<div class="blog__content">
<aside class="toc">
<h2>Table of Contents</h2>
<ul>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#1-setup-debian-gnu-linux">1. Setup Debian GNU&#x2F;Linux</a>
<ul>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#1-1-give-your-account-sudo-privileges">1.1) Give your account sudo privileges</a>
</li>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#1-2-install-and-setup-firewall-ufw">1.2) Install and setup firewall(ufw)</a>
</li>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#1-3-secure-ssh">1.3) Secure SSH</a>
</li>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#1-3-1-generate-key-pair">1.3.1) Generate key pair</a>
</li>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#1-3-2-setup-public-key-authentication">1.3.2) Setup public-key authentication</a>
</li>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#1-3-3-disable-ssh-password-authentication">1.3.3) Disable SSH password authentication</a>
</li>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#1-3-install-and-setup-fail2ban">1.3) Install and setup fail2ban</a>
<ul>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#1-3-1-install-fail2ban">1.3.1) Install fail2ban</a>
</li>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#1-3-2-enable-fail2ban-for-sshd">1.3.2) Enable fail2ban for sshd</a>
</li>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#1-3-3-configure-fail2ban-to-start-on-boot">1.3.3) Configure fail2ban to start on boot</a>
</li>
</ul>
</li>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#1-4-install-and-setup-nginx">1.4) Install and setup nginx</a>
<ul>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#1-4-1-install-nginx">1.4.1) Install nginx:</a>
</li>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#1-4-2-allow-web-traffic-open-ports-80-and-443">1.4.2) Allow web traffic: open ports 80 and 443</a>
</li>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#1-4-2-configure-nginx-to-start-on-boot">1.4.2) Configure nginx to start on boot</a>
</li>
</ul>
</li>
</ul>
</li>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#2-deploy-website">2) Deploy website</a>
<ul>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#2-1-install-the-webpage-on-the-server">2.1) Install the webpage on the server</a>
</li>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#2-2-serve-webpage-on-a-custom-domain">2.2) Serve webpage on a custom domain</a>
<ul>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#2-2-1-buy-a-domain-if-you-don-t-own-one-already">2.2.1) Buy a domain if you don&#x27;t own one already</a>
</li>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#2-2-2-go-to-the-domain-s-dns-dashboard-and-add-the-following-record">2.2.2) Go to the domain&#x27;s DNS dashboard and add the following record</a>
</li>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#2-2-3-setup-nginx-to-serve-the-website-at-http-your-domain">2.2.3) Setup nginx to serve the website at http:&#x2F;&#x2F;&lt;your-domain&gt;</a>
</li>
</ul>
</li>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#2-3-install-certbot-to-set-up-https">2.3) Install certbot to set up HTTPS</a>
<ul>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#2-3-1-install-certbot">2.3.1) Install certbot</a>
</li>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#2-3-2-get-a-certificate-for-your-domain">2.3.2) Get a certificate for &lt;your-domain&gt;</a>
</li>
<li>
<a href="https://librepages.org/blog/2022-09-10-how-to-publish-website-without-librepages/#2-3-3-setup-cronjob-to-automate-ssl-certificate-renewals">2.3.3) Setup cronjob to automate SSL certificate renewals</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</aside>
<p>In this <del>blog post</del> tutorial, I'll show you how to deploy a personal
website. LibrePages automates everything that is discussed in this
tutorial and lets you focus on creating content. Automation is good
but knowing how to do it manually using industry standard
technologies always helps!</p>
<p>We will be using the following technologies to deploy our website:</p>
<ol>
<li><a href="https://debian.org">GNU/Linux server(Debian)</a></li>
<li><a href="https://www.nginx.com/">Nginx</a> (webs server)</li>
<li><a href="https://letsencrypt.org/">Let's Encrypt</a> (for HTTPS)</li>
</ol>
<p>Let's get started!</p>
<h2 id="1-setup-debian-gnu-linux">1. Setup Debian GNU/Linux<a class="zola-anchor" href="#1-setup-debian-gnu-linux" aria-label="Anchor link for: 1-setup-debian-gnu-linux"
><span class="anchor-icon">#</span></a
>
</h2>
<p>We are going to start with a fresh GNU/Linux installation, you could get
one from a cloud provider like <a href="https://www.digitalocean.com">Digital
Ocean</a> (not affiliated).</p>
<h3 id="1-1-give-your-account-sudo-privileges">1.1) Give your account <code>sudo</code> privileges<a class="zola-anchor" href="#1-1-give-your-account-sudo-privileges" aria-label="Anchor link for: 1-1-give-your-account-sudo-privileges"
><span class="anchor-icon">#</span></a
>
</h3>
<p>On GNU/Linux systems, the <code>root</code> account is the most powerful user account.
It is good practice to avoid working as <code>root</code> since a careless mistake
could wipe the entire system out.</p>
<p><code>sudo</code> give the ability to execute commands with <code>root</code> capabilities
from a lower-privileged account. Let's make our account sudo capable:</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">su </span><span style="color:#65737e;"># become root
</span><span>
</span><span style="color:#65737e;"># add `realaravinth`, my account` to `sudo` group to be able to use `sudo`
</span><span style="color:#bf616a;">usermod -aG</span><span> sudo realaravinth </span><span style="color:#65737e;"># my account is called `realaravinth`, replace it with yours
</span><span style="color:#96b5b4;">exit
</span><span style="color:#bf616a;">$</span><span> exit
</span></code></pre>
<p>Log out and log back in.</p>
<h3 id="1-2-install-and-setup-firewall-ufw">1.2) Install and setup firewall(<code>ufw</code>)<a class="zola-anchor" href="#1-2-install-and-setup-firewall-ufw" aria-label="Anchor link for: 1-2-install-and-setup-firewall-ufw"
><span class="anchor-icon">#</span></a
>
</h3>
<p><a href="https://wiki.ubuntu.com/UncomplicatedFirewall">Uncomplicated
Firewall(<code>ufw</code>)</a> is a
popular firewall that is easy to set up and maintain. For most
installations, this should be enough. System administrators use
firewalls to open only the ports that they think should receive traffic
from external networks. Without it, all ports will be open, causing a
security nightmare.</p>
<p>We will require standard SSH (22), and the standard web ports (80 and
443). A comprehensive list of services and the list of ports the listen
on is available at <code>/etc/services</code>.</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">$</span><span> sudo apt update &amp;&amp; </span><span style="color:#bf616a;">apt</span><span> upgrade </span><span style="color:#65737e;"># update system
</span><span style="color:#bf616a;">$</span><span> sudo apt install ufw </span><span style="color:#65737e;"># we are using `ufw` for the firewall
</span><span style="color:#bf616a;">$</span><span> sudo ufw allow ssh </span><span style="color:#65737e;"># allow SSH traffic on port 22, required to log into the server
</span><span style="color:#bf616a;">$</span><span> sudo ufw enable </span><span style="color:#65737e;"># deploy firewall
</span></code></pre>
<h3 id="1-3-secure-ssh">1.3) Secure SSH<a class="zola-anchor" href="#1-3-secure-ssh" aria-label="Anchor link for: 1-3-secure-ssh"
><span class="anchor-icon">#</span></a
>
</h3>
<p>SSH allows remote access to our servers over secure, encrypted channels.
By default, users can log in with their password using SSH. But password
authentication <a href="https://wiki.archlinux.org/title/OpenSSH#Protecting_against_brute_force_attacks">is susceptible to brute force
attacks</a>,
so we should <a href="https://wiki.archlinux.org/title/OpenSSH#Force_public_key_authentication">disable password logins on our server and only allow
public-key authentication
only</a>.</p>
<h3 id="1-3-1-generate-key-pair">1.3.1) Generate key pair<a class="zola-anchor" href="#1-3-1-generate-key-pair" aria-label="Anchor link for: 1-3-1-generate-key-pair"
><span class="anchor-icon">#</span></a
>
</h3>
<p>On your local computer, generate an SSH key pair:</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">$</span><span> ssh-keygen
</span><span style="color:#bf616a;">Generating</span><span> public/private rsa key pair.
</span><span style="color:#bf616a;">Enter</span><span> file in which to save the key (/home/realaravinth/.ssh/id_rsa)</span><span style="color:#96b5b4;">:
</span><span style="color:#bf616a;">Enter</span><span> passphrase (empty for no passphrase)</span><span style="color:#96b5b4;">:
</span><span style="color:#bf616a;">Enter</span><span> same passphrase again:
</span><span style="color:#bf616a;">Your</span><span> identification has been saved in /home/realaravinth/.ssh/id_rsa
</span><span style="color:#bf616a;">Your</span><span> public key has been saved in /home/realaravinth/.ssh/id_rsa.pub
</span><span style="color:#bf616a;">The</span><span> key fingerprint is:
</span><span style="color:#bf616a;">SHA256:i2DE1b9BQb9DqV0r6O9MfPeVqUwfww1/T8wIXL2Xqdo</span><span> realaravinth@myserver.com
</span><span style="color:#bf616a;">The</span><span> key&#39;</span><span style="color:#a3be8c;">s random art image is:
</span><span style="color:#a3be8c;">+---[RSA 3072]----+
</span><span style="color:#a3be8c;">| .. .o. |
</span><span style="color:#a3be8c;">| . . . .. . . |
</span><span style="color:#a3be8c;">| o o + o .|
</span><span style="color:#a3be8c;">| . o* + .+|
</span><span style="color:#a3be8c;">| o S ooB o+.|
</span><span style="color:#a3be8c;">| . . . o.. +o*=|
</span><span style="color:#a3be8c;">| . . . ooo*X|
</span><span style="color:#a3be8c;">| +=.ooB|
</span><span style="color:#a3be8c;">| o+E .o|
</span><span style="color:#a3be8c;">+----[SHA256]-----+
</span></code></pre>
<p>Set a strong password the program prompts for one and save it somewhere
safe. Your public key will be at <code>~/.ssh/id_rsa.pub</code> and your private key at
<code>~/.ssh/id_rsa</code>. <strong>Never share the private key with anyone</strong>.</p>
<h3 id="1-3-2-setup-public-key-authentication">1.3.2) Setup public-key authentication<a class="zola-anchor" href="#1-3-2-setup-public-key-authentication" aria-label="Anchor link for: 1-3-2-setup-public-key-authentication"
><span class="anchor-icon">#</span></a
>
</h3>
<p>We have to copy the public key that we generated in the previous setup
onto our server:</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">$</span><span> ssh-copy-id</span><span style="color:#bf616a;"> -i ~</span><span>/.ssh/id_rsa.pub myserver.com
</span><span style="color:#bf616a;">/usr/bin/ssh-copy-id:</span><span> INFO: Source of key(s) </span><span style="color:#bf616a;">to</span><span> be installed: &quot;</span><span style="color:#a3be8c;">/home/realaravinth/.ssh/id_rsa.pub</span><span>&quot;
</span><span style="color:#bf616a;">/usr/bin/ssh-copy-id:</span><span> INFO: attempting to log in with the new key(s)</span><span style="color:#bf616a;">,</span><span> to filter out any that are already installed
</span><span style="color:#bf616a;">/usr/bin/ssh-copy-id:</span><span> INFO: 1 key(s) </span><span style="color:#bf616a;">remain</span><span> to be installed -- if you are prompted now it is to install the new keys
</span><span style="color:#bf616a;">realaravinth@myserver.com</span><span>&#39;</span><span style="color:#a3be8c;">s password:
</span><span style="color:#a3be8c;">
</span><span style="color:#a3be8c;">Number of key(s) added: 1
</span><span style="color:#a3be8c;">
</span><span style="color:#a3be8c;">Now try logging into the machine, with: &quot;ssh </span><span>&#39;</span><span style="color:#bf616a;">myserver.com</span><span>&#39;</span><span style="color:#a3be8c;">&quot;
</span><span style="color:#a3be8c;">and check to make sure that only the key(s) you wanted were added.
</span></code></pre>
<h3 id="1-3-3-disable-ssh-password-authentication">1.3.3) Disable SSH password authentication<a class="zola-anchor" href="#1-3-3-disable-ssh-password-authentication" aria-label="Anchor link for: 1-3-3-disable-ssh-password-authentication"
><span class="anchor-icon">#</span></a
>
</h3>
<blockquote>
<p><strong>NOTE: Verify you can log into your account before proceeding</strong></p>
</blockquote>
<p>Now that we have a private-key authentication setup on both the client and
the server, let's disable password authentication on the server:</p>
<p>Open <code>/etc/ssh/sshd_config</code> and add the following lines:</p>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>PubkeyAuthentication yes
</span><span>PasswordAuthentication no
</span></code></pre>
<p>And restart the SSH server:</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">$</span><span> sudo systemctl restart sshd
</span></code></pre>
<h3 id="1-3-install-and-setup-fail2ban">1.3) Install and setup <code>fail2ban</code><a class="zola-anchor" href="#1-3-install-and-setup-fail2ban" aria-label="Anchor link for: 1-3-install-and-setup-fail2ban"
><span class="anchor-icon">#</span></a
>
</h3>
<p>We will be using
<a href="https://www.fail2ban.org/wiki/index.php/Main_Page"><code>fail2ban</code></a> for
intrusion prevention by blacklisting entities (users, bots, etc.) based
on failed login attempts.</p>
<h4 id="1-3-1-install-fail2ban">1.3.1) Install <code>fail2ban</code><a class="zola-anchor" href="#1-3-1-install-fail2ban" aria-label="Anchor link for: 1-3-1-install-fail2ban"
><span class="anchor-icon">#</span></a
>
</h4>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">$</span><span> sudo apt install fail2ban
</span></code></pre>
<h4 id="1-3-2-enable-fail2ban-for-sshd">1.3.2) Enable <code>fail2ban</code> for <code>sshd</code><a class="zola-anchor" href="#1-3-2-enable-fail2ban-for-sshd" aria-label="Anchor link for: 1-3-2-enable-fail2ban-for-sshd"
><span class="anchor-icon">#</span></a
>
</h4>
<p>Open <code>fail2ban</code> configuration at <code>/etc/fail2ban/jail.conf</code> and add the following lines:</p>
<pre data-lang="yml" style="background-color:#2b303b;color:#c0c5ce;" class="language-yml "><code class="language-yml" data-lang="yml"><span>[</span><span style="color:#a3be8c;">sshd</span><span>]
</span><span style="color:#a3be8c;">enabled = true
</span></code></pre>
<h4 id="1-3-3-configure-fail2ban-to-start-on-boot">1.3.3) Configure <code>fail2ban</code> to start on boot<a class="zola-anchor" href="#1-3-3-configure-fail2ban-to-start-on-boot" aria-label="Anchor link for: 1-3-3-configure-fail2ban-to-start-on-boot"
><span class="anchor-icon">#</span></a
>
</h4>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">$</span><span> sudo systemctl enable fail2ban
</span><span style="color:#bf616a;">$</span><span> sudo systemctl start fail2ban
</span></code></pre>
<h3 id="1-4-install-and-setup-nginx">1.4) Install and setup <code>nginx</code><a class="zola-anchor" href="#1-4-install-and-setup-nginx" aria-label="Anchor link for: 1-4-install-and-setup-nginx"
><span class="anchor-icon">#</span></a
>
</h3>
<p><code>nginx</code> is a popular web server that can be used to serve static sites.
It is fast, stable, and easy to set up.</p>
<p>To install, run the following command:</p>
<h4 id="1-4-1-install-nginx">1.4.1) Install <code>nginx</code>:<a class="zola-anchor" href="#1-4-1-install-nginx" aria-label="Anchor link for: 1-4-1-install-nginx"
><span class="anchor-icon">#</span></a
>
</h4>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">$</span><span> sudo apt install nginx
</span></code></pre>
<h4 id="1-4-2-allow-web-traffic-open-ports-80-and-443">1.4.2) Allow web traffic: open ports <code>80</code> and <code>443</code><a class="zola-anchor" href="#1-4-2-allow-web-traffic-open-ports-80-and-443" aria-label="Anchor link for: 1-4-2-allow-web-traffic-open-ports-80-and-443"
><span class="anchor-icon">#</span></a
>
</h4>
<p>Ports <code>80</code> is the default for HTTP and <code>443</code> for HTTPS. To serve
web traffic, we'll have to Configure <code>ufw</code> to accept traffic on them:</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">$</span><span> sudo ufw allow 80 </span><span style="color:#65737e;"># open ports 80 HTTP traffic
</span><span style="color:#bf616a;">$</span><span> sudo ufw allow 443 </span><span style="color:#65737e;"># open ports 443 for HTTPS traffic
</span></code></pre>
<h4 id="1-4-2-configure-nginx-to-start-on-boot">1.4.2) Configure <code>nginx</code> to start on boot<a class="zola-anchor" href="#1-4-2-configure-nginx-to-start-on-boot" aria-label="Anchor link for: 1-4-2-configure-nginx-to-start-on-boot"
><span class="anchor-icon">#</span></a
>
</h4>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">$</span><span> sudo systemctl enable nginx </span><span style="color:#65737e;"># automatically start nginx on boot
</span><span style="color:#bf616a;">$</span><span> sudo systemctl start nginx </span><span style="color:#65737e;"># start nginx server
</span></code></pre>
<p>And verify it works:</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">$</span><span> curl localhost
</span><span>&lt;!DOCTYPE </span><span style="color:#bf616a;">html</span><span>&gt;
</span><span>&lt;html&gt;
</span><span>&lt;head&gt;
</span><span>&lt;title&gt;Welcome </span><span style="color:#bf616a;">to</span><span> nginx!&lt;/title&gt;
</span><span>&lt;style&gt;
</span><span> </span><span style="color:#bf616a;">body </span><span>{
</span><span> width: 35em;
</span><span> margin: 0 auto;
</span><span> font-family: Tahoma, Verdana, Arial, sans-serif;
</span><span> }
</span><span>&lt;/style&gt;
</span><span>&lt;/head&gt;
</span><span>&lt;body&gt;
</span><span>&lt;h1&gt;Welcome </span><span style="color:#bf616a;">to</span><span> nginx!&lt;/h1&gt;
</span><span>&lt;p&gt;If </span><span style="color:#bf616a;">you</span><span> see this page, the nginx web server is successfully installed and
</span><span style="color:#bf616a;">working.</span><span> Further configuration is required.&lt;/p&gt;
</span><span>
</span><span>&lt;p&gt;For </span><span style="color:#bf616a;">online</span><span> documentation and support please refer to
</span><span>&lt;a </span><span style="color:#bf616a;">href</span><span>=&quot;</span><span style="color:#a3be8c;">http://nginx.org/</span><span>&quot;&gt;nginx.org&lt;/a&gt;.&lt;br/&gt;
</span><span style="color:#bf616a;">Commercial</span><span> support is available at
</span><span>&lt;a </span><span style="color:#bf616a;">href</span><span>=&quot;</span><span style="color:#a3be8c;">http://nginx.com/</span><span>&quot;&gt;nginx.com&lt;/a&gt;.&lt;/p&gt;
</span><span>
</span><span>&lt;p&gt;&lt;em&gt;Thank </span><span style="color:#bf616a;">you</span><span> for using nginx.&lt;/em&gt;&lt;/p&gt;
</span><span>&lt;/body&gt;
</span><span>&lt;/html&gt;
</span></code></pre>
<p><code>nginx</code> is working!</p>
<h2 id="2-deploy-website">2) Deploy website<a class="zola-anchor" href="#2-deploy-website" aria-label="Anchor link for: 2-deploy-website"
><span class="anchor-icon">#</span></a
>
</h2>
<p>For this demo, we'll deploy a single file(<code>index.html</code>)
HTML website.</p>
<h3 id="2-1-install-the-webpage-on-the-server">2.1) Install the webpage on the server<a class="zola-anchor" href="#2-1-install-the-webpage-on-the-server" aria-label="Anchor link for: 2-1-install-the-webpage-on-the-server"
><span class="anchor-icon">#</span></a
>
</h3>
<p>Edit <code>/var/www/html/index.html</code> and add the following HTML to it:</p>
<pre data-lang="html" style="background-color:#2b303b;color:#c0c5ce;" class="language-html "><code class="language-html" data-lang="html"><span>&lt;!</span><span style="color:#b48ead;">DOCTYPE </span><span style="color:#d08770;">html</span><span>&gt;
</span><span>&lt;</span><span style="color:#bf616a;">html</span><span>&gt;
</span><span> &lt;</span><span style="color:#bf616a;">head</span><span>&gt;
</span><span> &lt;</span><span style="color:#bf616a;">title</span><span>&gt;My cool website!&lt;/</span><span style="color:#bf616a;">title</span><span>&gt;
</span><span> &lt;/</span><span style="color:#bf616a;">head</span><span>&gt;
</span><span> &lt;</span><span style="color:#bf616a;">body</span><span>&gt;
</span><span> &lt;</span><span style="color:#bf616a;">h1</span><span>&gt;Welcome to my website! o/&lt;/</span><span style="color:#bf616a;">h1</span><span>&gt;
</span><span> &lt;/</span><span style="color:#bf616a;">body</span><span>&gt;
</span><span>&lt;/</span><span style="color:#bf616a;">html</span><span>&gt;
</span></code></pre>
<p>The webpage should now be available on localhost, and we should see it when we run the following command:</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">$</span><span> curl localhost
</span><span>&lt;!DOCTYPE </span><span style="color:#bf616a;">html</span><span>&gt;
</span><span>&lt;html&gt;
</span><span> &lt;head&gt;
</span><span> &lt;title&gt;My </span><span style="color:#bf616a;">cool</span><span> website!&lt;/title&gt;
</span><span> &lt;/head&gt;
</span><span> &lt;body&gt;
</span><span> &lt;h1&gt;Welcome </span><span style="color:#bf616a;">to</span><span> my website! o/&lt;/h1&gt;
</span><span> &lt;/body&gt;
</span><span>&lt;/html&gt;
</span></code></pre>
<h3 id="2-2-serve-webpage-on-a-custom-domain">2.2) Serve webpage on a custom domain<a class="zola-anchor" href="#2-2-serve-webpage-on-a-custom-domain" aria-label="Anchor link for: 2-2-serve-webpage-on-a-custom-domain"
><span class="anchor-icon">#</span></a
>
</h3>
<h4 id="2-2-1-buy-a-domain-if-you-don-t-own-one-already">2.2.1) Buy a domain if you don't own one already<a class="zola-anchor" href="#2-2-1-buy-a-domain-if-you-don-t-own-one-already" aria-label="Anchor link for: 2-2-1-buy-a-domain-if-you-don-t-own-one-already"
><span class="anchor-icon">#</span></a
>
</h4>
<h4 id="2-2-2-go-to-the-domain-s-dns-dashboard-and-add-the-following-record">2.2.2) Go to the domain's DNS dashboard and add the following record<a class="zola-anchor" href="#2-2-2-go-to-the-domain-s-dns-dashboard-and-add-the-following-record" aria-label="Anchor link for: 2-2-2-go-to-the-domain-s-dns-dashboard-and-add-the-following-record"
><span class="anchor-icon">#</span></a
>
</h4>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>@ A 300 &lt;your server IP address&gt;
</span></code></pre>
<h4 id="2-2-3-setup-nginx-to-serve-the-website-at-http-your-domain">2.2.3) Setup <code>nginx</code> to serve the website at <code>http://&lt;your-domain&gt;</code><a class="zola-anchor" href="#2-2-3-setup-nginx-to-serve-the-website-at-http-your-domain" aria-label="Anchor link for: 2-2-3-setup-nginx-to-serve-the-website-at-http-your-domain"
><span class="anchor-icon">#</span></a
>
</h4>
<p>Open <code>/etc/nginx/sites-available/your-domain</code> and add the following:</p>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>server {
</span><span> # serve website on port 80
</span><span> listen [::]:80;
</span><span> listen 80;
</span><span>
</span><span> # write error logs to file
</span><span> error_log /var/log/nginx/&lt;your-domain&gt;.error.log;
</span><span> # write access logs to file
</span><span> access_log /var/log/nginx/&lt;your-domain&gt;.access.log;
</span><span>
</span><span> # serve only on this domain:
</span><span> server_name &lt;your-domain&gt;; # replace me
</span><span>
</span><span>
</span><span> # use files from this directory
</span><span> root /var/www/html/;
</span><span>
</span><span> # remove .html from URL; it is cleaner this way
</span><span> rewrite ^(/.*)\.html(\?.*)?$ $1$2 permanent;
</span><span>
</span><span> # when a request is received, try the index.html in the directory
</span><span> # or $uri.html
</span><span> try_files $uri/index.html $uri.html $uri/ $uri =404;
</span><span>}
</span></code></pre>
<p>It is good practice to have all <code>nginx</code> deployment configurations in
<code>/etc/nginx/sites-available/</code> directory and link production websites to
`/etc/nginx/sites-enabled directory. Doing so allows you to
work-in-progress configurations or delete deployments without losing
the configuration files.</p>
<p>Let's enable <code>&lt;your-domain&gt;</code></p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">$</span><span> sudo ln</span><span style="color:#bf616a;"> -s</span><span> /etc/nginx/sites-available/&lt;your-domain&gt; /etc/nginx/sites-enabled/&lt;your-domain&gt;
</span></code></pre>
<p>Verify configurations before deploying, <code>nginx</code> has a command
to do it:</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">$</span><span> sudo nginx</span><span style="color:#bf616a;"> -t
</span></code></pre>
<p>If there are no errors, reload <code>nginx</code> to deploy the website:</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">$</span><span> sudo nginx</span><span style="color:#bf616a;"> -s</span><span> reload
</span></code></pre>
<p>Your webpage should now be accessible at <code>http://&lt;your-domain&gt;</code>!</p>
<h3 id="2-3-install-certbot-to-set-up-https">2.3) Install <code>certbot</code> to set up HTTPS<a class="zola-anchor" href="#2-3-install-certbot-to-set-up-https" aria-label="Anchor link for: 2-3-install-certbot-to-set-up-https"
><span class="anchor-icon">#</span></a
>
</h3>
<p>HTTP is insecure. We'll have to set up SSL to serve our website using
HTTPS. To do that, we will be using <a href="https://letsencrypt.org/">Let's
Encrypt</a> a popular nonprofit certificate
authority to get our SSL certificates.</p>
<p>SSL certificates come with set lifetimes, so we renew them before they expire. The process, when done manually, is demanding: you
will have to log in every three months and renew the
certificate. If you fail or forget it, your visitors will see security
warnings on your website.</p>
<p>Thankfully, there is a way to automate this process through
<a href="https://certbot.eff.org/"><code>certbot</code></a></p>
<h4 id="2-3-1-install-certbot">2.3.1) Install <code>certbot</code><a class="zola-anchor" href="#2-3-1-install-certbot" aria-label="Anchor link for: 2-3-1-install-certbot"
><span class="anchor-icon">#</span></a
>
</h4>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">$</span><span> sudo apt install certbot python3-certbot-nginx
</span></code></pre>
<h4 id="2-3-2-get-a-certificate-for-your-domain">2.3.2) Get a certificate for <code>&lt;your-domain&gt;</code><a class="zola-anchor" href="#2-3-2-get-a-certificate-for-your-domain" aria-label="Anchor link for: 2-3-2-get-a-certificate-for-your-domain"
><span class="anchor-icon">#</span></a
>
</h4>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">$</span><span> sudo certbot</span><span style="color:#bf616a;"> --nginx -d </span><span>&lt;your-domain&gt;
</span></code></pre>
<p><code>certbot</code> will prompt you for an email ID, and ask you to accept their
terms and conditions, privacy policy, etc. Be sure to read them before
agreeing to them. It will then try to authenticate your domain ownership
using the <a href="https://en.wikipedia.org/wiki/Automatic_Certificate_Management_Environment">ACME
protocol</a>.
By configuring the DNS to point to our server and by telling <code>nginx</code> at
that domain.</p>
<p>When it has verified ownership, it will automatically issue, deploy the
certificate on <code>nginx</code> and setup redirects.</p>
<h4 id="2-3-3-setup-cronjob-to-automate-ssl-certificate-renewals">2.3.3) Setup cronjob to automate SSL certificate renewals<a class="zola-anchor" href="#2-3-3-setup-cronjob-to-automate-ssl-certificate-renewals" aria-label="Anchor link for: 2-3-3-setup-cronjob-to-automate-ssl-certificate-renewals"
><span class="anchor-icon">#</span></a
>
</h4>
<p>Become root and edit crontab</p>
<pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#bf616a;">$</span><span> su
</span><span style="color:#bf616a;">crontab -e
</span></code></pre>
<p>Add the following job and exit:</p>
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>0 */12 * * * certbot -n --nginx renew
</span></code></pre>
<p>It will attempt to renew SSL certificates every 12 hours. If a the
certificate is due for renewal, <code>certbot</code> will go through the ACME
challenge, get the new certificates and automatically deploy them for
you.</p>
<p>Now our GNU/Linux server is configured and ready to serve our website at
<code>http://&lt;your-website&gt;</code>!</p>
</div>
<br />
<br />
<div class="blog__post-tag-container">
<a class="blog__post-tag" href="/tags/bare-metal">#bare-metal</a>
<a class="blog__post-tag" href="/tags/nginx">#nginx</a>
<a class="blog__post-tag" href="/tags/jamstack">#JAMStack</a>
<a class="blog__post-tag" href="/tags/lets-encrypt">#lets-encrypt</a>
<a class="blog__post-tag" href="/tags/self-hosting">#self-hosting</a>
</div>
</div>
<script src="https://librepages.org/js/copy.js?h=983f829c3d114fd1d3a08ffab2982a2884dec7fe62b8aedb545c1a4145a3c4f5"></script>
</main>
<footer>
<div class="footer__container">
<!-- <div class="footer__column"> --->
<p class="footer__column license__conatiner">
All text <a
class="license__link"
rel="noreferrer"
href="http://creativecommons.org/licenses/by-sa/4.0/"
target="_blank"
>&nbsp;CC-BY-SA&nbsp;</a
>
&amp; code
<a
class="license__link"
rel="noreferrer"
href="https://www.gnu.org/licenses/agpl-3.0.en.html"
target="_blank"
>&nbsp;AGPL&nbsp;</a
>
</p>
<!-- </div> -->
<div class="footer__column--center">
<a href="/blog/atom.xml" target="_blank" rel="noopener" title="RSS">
<img
src="https://librepages.org/icons/rss.svg?h=f6cd584bdbcd2eb4d1b8b84c9cf083ef45f772167c33fdcee754b35ae8ff4c7d"
class="footer__icon"
alt="Email icon"
/>
</a>
</div>
<div class="footer__column">
<a href="/about" title="About">About</a>
<a href="/coc" title="Code of Conduct">CoC</a>
<span class="footer__column-divider--mobile-only">|</span>
<a href="/legalese" title="Legalese">Legalese</a>
<a href="/privacy-policy" title="Privacy Policy">Privacy</a>
<span class="footer__column-divider--mobile-only">|</span>
<a
href="https://git.batsense.net/LibrePages"
rel="noreferrer"
target="_blank"
title="Status"
>Source Code</a
>
<!--
<a href="/tos" title="Terms of Service">ToS</a>
-->
</div>
</div>
</footer>
</div>
</body>
</html>