<metaname="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?"/>
<metaproperty="og:title"content="How to deploy a website WITHOUT LibrePages | LibrePages: JAMstack platform with focus on privacy and speed"/>
<metaproperty="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"
<ahref="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>
<ahref="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>
<ahref="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>
<ahref="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>
<ahref="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>
<ahref="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>
<ahref="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>
<ahref="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>
<ahref="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>
<ahref="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>
<ahref="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't own one already</a>
</li>
<li>
<ahref="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's DNS dashboard and add the following record</a>
<ahref="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://<your-domain></a>
<ahref="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>
<ahref="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 <your-domain></a>
</li>
<li>
<ahref="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>
<h2id="1-setup-debian-gnu-linux">1. Setup Debian GNU/Linux<aclass="zola-anchor"href="#1-setup-debian-gnu-linux"aria-label="Anchor link for: 1-setup-debian-gnu-linux"
><spanclass="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 <ahref="https://www.digitalocean.com">Digital
Ocean</a> (not affiliated).</p>
<h3id="1-1-give-your-account-sudo-privileges">1.1) Give your account <code>sudo</code> privileges<aclass="zola-anchor"href="#1-1-give-your-account-sudo-privileges"aria-label="Anchor link for: 1-1-give-your-account-sudo-privileges"
><spanclass="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>
<predata-lang="bash"style="background-color:#2b303b;color:#c0c5ce;"class="language-bash "><codeclass="language-bash"data-lang="bash"><spanstyle="color:#bf616a;">su </span><spanstyle="color:#65737e;"># become root
</span><span>
</span><spanstyle="color:#65737e;"># add `realaravinth`, my account` to `sudo` group to be able to use `sudo`
</span><spanstyle="color:#bf616a;">usermod -aG</span><span> sudo realaravinth </span><spanstyle="color:#65737e;"># my account is called `realaravinth`, replace it with yours
<h3id="1-2-install-and-setup-firewall-ufw">1.2) Install and setup firewall(<code>ufw</code>)<aclass="zola-anchor"href="#1-2-install-and-setup-firewall-ufw"aria-label="Anchor link for: 1-2-install-and-setup-firewall-ufw"
on is available at <code>/etc/services</code>.</p>
<predata-lang="bash"style="background-color:#2b303b;color:#c0c5ce;"class="language-bash "><codeclass="language-bash"data-lang="bash"><spanstyle="color:#bf616a;">$</span><span> sudo apt update &&</span><spanstyle="color:#bf616a;">apt</span><span> upgrade </span><spanstyle="color:#65737e;"># update system
</span><spanstyle="color:#bf616a;">$</span><span> sudo apt install ufw </span><spanstyle="color:#65737e;"># we are using `ufw` for the firewall
</span><spanstyle="color:#bf616a;">$</span><span> sudo ufw allow ssh </span><spanstyle="color:#65737e;"># allow SSH traffic on port 22, required to log into the server
</span><spanstyle="color:#bf616a;">Enter</span><span> file in which to save the key (/home/realaravinth/.ssh/id_rsa)</span><spanstyle="color:#96b5b4;">:
</span><spanstyle="color:#bf616a;">Enter</span><span> passphrase (empty for no passphrase)</span><spanstyle="color:#96b5b4;">:
</span><spanstyle="color:#bf616a;">Enter</span><span> same passphrase again:
</span><spanstyle="color:#bf616a;">Your</span><span> identification has been saved in /home/realaravinth/.ssh/id_rsa
</span><spanstyle="color:#bf616a;">Your</span><span> public key has been saved in /home/realaravinth/.ssh/id_rsa.pub
</span><spanstyle="color:#bf616a;">The</span><span> key fingerprint is:
</span><spanstyle="color:#bf616a;">/usr/bin/ssh-copy-id:</span><span> INFO: Source of key(s) </span><spanstyle="color:#bf616a;">to</span><span> be installed: "</span><spanstyle="color:#a3be8c;">/home/realaravinth/.ssh/id_rsa.pub</span><span>"
</span><spanstyle="color:#bf616a;">/usr/bin/ssh-copy-id:</span><span> INFO: attempting to log in with the new key(s)</span><spanstyle="color:#bf616a;">,</span><span> to filter out any that are already installed
</span><spanstyle="color:#bf616a;">/usr/bin/ssh-copy-id:</span><span> INFO: 1 key(s) </span><spanstyle="color:#bf616a;">remain</span><span> to be installed -- if you are prompted now it is to install the new keys
</span><spanstyle="color:#a3be8c;">Number of key(s) added: 1
</span><spanstyle="color:#a3be8c;">
</span><spanstyle="color:#a3be8c;">Now try logging into the machine, with: "ssh </span><span>'</span><spanstyle="color:#bf616a;">myserver.com</span><span>'</span><spanstyle="color:#a3be8c;">"
</span><spanstyle="color:#a3be8c;">and check to make sure that only the key(s) you wanted were added.
</span></code></pre>
<h3id="1-3-3-disable-ssh-password-authentication">1.3.3) Disable SSH password authentication<aclass="zola-anchor"href="#1-3-3-disable-ssh-password-authentication"aria-label="Anchor link for: 1-3-3-disable-ssh-password-authentication"
><spanclass="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>
<h3id="1-3-install-and-setup-fail2ban">1.3) Install and setup <code>fail2ban</code><aclass="zola-anchor"href="#1-3-install-and-setup-fail2ban"aria-label="Anchor link for: 1-3-install-and-setup-fail2ban"
<h4id="1-3-2-enable-fail2ban-for-sshd">1.3.2) Enable <code>fail2ban</code> for <code>sshd</code><aclass="zola-anchor"href="#1-3-2-enable-fail2ban-for-sshd"aria-label="Anchor link for: 1-3-2-enable-fail2ban-for-sshd"
<h4id="1-3-3-configure-fail2ban-to-start-on-boot">1.3.3) Configure <code>fail2ban</code> to start on boot<aclass="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"
<h3id="1-4-install-and-setup-nginx">1.4) Install and setup <code>nginx</code><aclass="zola-anchor"href="#1-4-install-and-setup-nginx"aria-label="Anchor link for: 1-4-install-and-setup-nginx"
><spanclass="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>
<h4id="1-4-1-install-nginx">1.4.1) Install <code>nginx</code>:<aclass="zola-anchor"href="#1-4-1-install-nginx"aria-label="Anchor link for: 1-4-1-install-nginx"
<h4id="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><aclass="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"
><spanclass="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>
</span><spanstyle="color:#bf616a;">$</span><span> sudo ufw allow 443 </span><spanstyle="color:#65737e;"># open ports 443 for HTTPS traffic
</span></code></pre>
<h4id="1-4-2-configure-nginx-to-start-on-boot">1.4.2) Configure <code>nginx</code> to start on boot<aclass="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><span><p><em>Thank </span><spanstyle="color:#bf616a;">you</span><span> for using nginx.</em></p>
</span><span></body>
</span><span></html>
</span></code></pre>
<p><code>nginx</code> is working!</p>
<h2id="2-deploy-website">2) Deploy website<aclass="zola-anchor"href="#2-deploy-website"aria-label="Anchor link for: 2-deploy-website"
><spanclass="anchor-icon">#</span></a
>
</h2>
<p>For this demo, we'll deploy a single file(<code>index.html</code>)
HTML website.</p>
<h3id="2-1-install-the-webpage-on-the-server">2.1) Install the webpage on the server<aclass="zola-anchor"href="#2-1-install-the-webpage-on-the-server"aria-label="Anchor link for: 2-1-install-the-webpage-on-the-server"
><spanclass="anchor-icon">#</span></a
>
</h3>
<p>Edit <code>/var/www/html/index.html</code> and add the following HTML to it:</p>
</span><span><</span><spanstyle="color:#bf616a;">h1</span><span>>Welcome to my website! o/</</span><spanstyle="color:#bf616a;">h1</span><span>>
</span><span><h1>Welcome </span><spanstyle="color:#bf616a;">to</span><span> my website! o/</h1>
</span><span></body>
</span><span></html>
</span></code></pre>
<h3id="2-2-serve-webpage-on-a-custom-domain">2.2) Serve webpage on a custom domain<aclass="zola-anchor"href="#2-2-serve-webpage-on-a-custom-domain"aria-label="Anchor link for: 2-2-serve-webpage-on-a-custom-domain"
><spanclass="anchor-icon">#</span></a
>
</h3>
<h4id="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<aclass="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"
><spanclass="anchor-icon">#</span></a
>
</h4>
<h4id="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<aclass="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"
><spanclass="anchor-icon">#</span></a
>
</h4>
<prestyle="background-color:#2b303b;color:#c0c5ce;"><code><span>@ A 300 <your server IP address>
<h4id="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://<your-domain></code><aclass="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"
<p>Your webpage should now be accessible at <code>http://<your-domain></code>!</p>
<h3id="2-3-install-certbot-to-set-up-https">2.3) Install <code>certbot</code> to set up HTTPS<aclass="zola-anchor"href="#2-3-install-certbot-to-set-up-https"aria-label="Anchor link for: 2-3-install-certbot-to-set-up-https"
><spanclass="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 <ahref="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
<h4id="2-3-2-get-a-certificate-for-your-domain">2.3.2) Get a certificate for <code><your-domain></code><aclass="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"
<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 <ahref="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>
<h4id="2-3-3-setup-cronjob-to-automate-ssl-certificate-renewals">2.3.3) Setup cronjob to automate SSL certificate renewals<aclass="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"
><spanclass="anchor-icon">#</span></a
>
</h4>
<p>Become root and edit crontab</p>
<predata-lang="bash"style="background-color:#2b303b;color:#c0c5ce;"class="language-bash "><codeclass="language-bash"data-lang="bash"><spanstyle="color:#bf616a;">$</span><span> su