<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Ting Wei]]></title><description><![CDATA[Ting Wei]]></description><link>https://blog.tingwei.fans</link><generator>RSS for Node</generator><lastBuildDate>Wed, 15 Apr 2026 04:06:40 GMT</lastBuildDate><atom:link href="https://blog.tingwei.fans/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Self Hosted Website on a Raspberry Pi (Part 3)]]></title><description><![CDATA[This is a three part tutorial that cites relevant tutorial guides that I found online that help me eventually achieve my final goal of self-hosting my personal website.

Part 1 - RPi Setup 
Part 2 - Flask App with Docker and Portainer
Part 3 - Domain...]]></description><link>https://blog.tingwei.fans/self-hosted-website-on-a-raspberry-pi-part-3</link><guid isPermaLink="true">https://blog.tingwei.fans/self-hosted-website-on-a-raspberry-pi-part-3</guid><category><![CDATA[Raspberry Pi]]></category><category><![CDATA[nginx]]></category><category><![CDATA[cloudflare]]></category><dc:creator><![CDATA[Fan Ting Wei]]></dc:creator><pubDate>Wed, 25 May 2022 15:47:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/A9_IsUtjHm4/upload/v1653493003774/ZRZkC0A4v.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is a three part tutorial that cites relevant tutorial guides that I found online that help me eventually achieve my final goal of self-hosting my personal website.</p>
<ul>
<li><a target="_blank" href="https://blog.tingwei.fans/self-hosted-website-on-a-raspberry-pi-part-1">Part 1 - RPi Setup</a> </li>
<li><a target="_blank" href="https://blog.tingwei.fans/self-hosted-website-on-a-raspberry-pi-part-2">Part 2 - Flask App with Docker and Portainer</a></li>
<li>Part 3 - Domain Name Management with Cloudflare and Nginx Proxy Manager (here)</li>
</ul>
<p>It is ok to ssh to your RPi with your network’s public IP address, but it has two problems when you want to host your own websites and applications:</p>
<ul>
<li>It is hard to remember random numbers to share the link. Imagine how lame it is to share your website as 112.16.12.32</li>
<li>It is not safe to expose your IP address since people can track you down with website such as <a target="_blank" href="https://check-host.net/ip-info?host=www.tingwei.fans">check-host.net</a></li>
</ul>
<p>That is why we will get a domain name and set up Cloudflare and Nginx Proxy Manager to direct traffic from domain into the applications in our RPi properly.</p>
<h1 id="heading-basic-server-security">Basic Server Security</h1>
<p>Since we are going to expose our server to the rest of the world, there are some basic firewalls to be placed. Even though our routers has port restrictions, it’s good to have software safeguards as well. Here we install ufw (Uncomplicated Firewall)</p>
<pre><code>$ sudo apt <span class="hljs-keyword">install</span> ufw
$ sudo ufw <span class="hljs-keyword">allow</span> <span class="hljs-number">22</span>
$ sudo ufw <span class="hljs-keyword">allow</span> <span class="hljs-number">5000</span> <span class="hljs-comment"># for our website docker port</span>
$ sudo ufw <span class="hljs-keyword">enable</span>
$ sudo ufw <span class="hljs-keyword">status</span>
<span class="hljs-keyword">Status</span>: active

<span class="hljs-keyword">To</span>                         <span class="hljs-keyword">Action</span>      <span class="hljs-keyword">From</span>
<span class="hljs-comment">--                         ------      ----</span>
<span class="hljs-number">22</span>                         <span class="hljs-keyword">ALLOW</span>       Anywhere
<span class="hljs-number">5000</span>                       <span class="hljs-keyword">ALLOW</span>       Anywhere
<span class="hljs-number">22</span> (v6)                    <span class="hljs-keyword">ALLOW</span>       Anywhere (v6)
<span class="hljs-number">5000</span> (v6)                  <span class="hljs-keyword">ALLOW</span>       Anywhere (v6)
</code></pre><h1 id="heading-get-a-domain-name">Get a Domain Name</h1>
<p>Firstly, you need to get a domain name for your web server. If you want to keep cost as low as possible, you could get one with <a target="_blank" href="https://www.noip.com/">noip.com</a>. For me, I simply can’t pass up my domain name so I got it off <a target="_blank" href="http://namecheap.com">namecheap.com</a>.</p>
<h1 id="heading-what-is-cloudflare">What is Cloudflare?</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653493275325/DHm6rywwd.png" alt="cloudflare.png" /></p>
<p>Cloudflare essentially is a massive worldwide network of servers in between users and a web server (interfaced by our router). First and foremost, they are a DNS (Domain Name System) provider, translating human readable domain names into numeric IP address meant for machine, which they can direct traffic to. </p>
<p>Providing this service means that they can also help to achieve two other things, security and speed. </p>
<p><strong>Security</strong>: Because they are one of the largest network, they scan the most IP addresses worldwide and have visibility of IP address with bad intention to be blocked ASAP.</p>
<p><strong>Speed</strong>: Since they have a really large network of servers, they can cache contents in your website so that users from the other side of the world will be served the content from the closest servers.</p>
<p><strong>And the best part - all of these are free!</strong></p>
<h1 id="heading-how-to-set-up-cloudflare">How to set up Cloudflare?</h1>
<p>So how do we set up Cloudflare with our domain name? Rather than rewriting another guide, I found that the most comprehensive tutorial came from their community tutorial, from pointing nameservers to Cloudflare.</p>
<p><a target="_blank" href="https://community.cloudflare.com/t/step-1-adding-your-domain-to-cloudflare/64309">Step 1: Adding your domain to Cloudflare - Tutorial - Cloudflare Community</a></p>
<p><a target="_blank" href="https://community.cloudflare.com/t/step-2-setting-up-ssl-with-cloudflare/94646">Step 2: Setting up SSL with Cloudflare - Tutorial - Cloudflare Community</a></p>
<p><a target="_blank" href="https://community.cloudflare.com/t/step-3-enabling-the-orange-cloud/52715">Step 3: Enabling the 'Orange Cloud' - Tutorial - Cloudflare Community</a></p>
<p>After you connected your domain to Cloudflare, you can click on your domain &gt; DNS to see all subdomain related to that domain. There might be alot of records or no records depending on your domain provider, and I usually remove all of them and create two of them:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653493290982/_g4huVxl1.png" alt="cloudflare dns.png" /></p>
<p>When you add records, put your <strong>public IP address</strong> we got from Part 1 in the tutorial under it. You could choose turn on the proxy status so that people cannot find your public IP address directly. </p>
<p>Some additional issues I had was </p>
<ul>
<li>Setting SSL policies to <strong>Flexible</strong> causing too many redirects - the solution is to change it to <strong>Strict</strong></li>
<li>Redirecting <a target="_blank" href="http://www.tingwei.fans"><code>www.tingwei.fans</code></a> to <code>tingwei.fans</code> using Rules functionality in Cloudflare</li>
<li>If you switch off your router, your public IP address might change. I am planning to implement this <a target="_blank" href="https://360techexplorer.com/cloudflare-dynamic-dns-raspberry-pi/">tutorial</a> for the server to automatically detect changes to my public address and update it to Cloudflare.</li>
</ul>
<h1 id="heading-what-is-nginx-proxy-manager">What is Nginx Proxy Manager?</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653493317065/RfoTFR-k5.png" alt="npm.png" /></p>
<p>Nginx Proxy Manager is actually an UI for Nginx, an open sourced software for web server and reverse proxying. If Cloudflare helps mediate external traffic from the World Wide Web to your router, Nginx mediate internal traffic from your router to different applications in your server.</p>
<p>Why not just use the Nginx directly? If you need more complicated settings for your NGINX, you could consider doing it manually, but Nginx Proxy Manager provided me a cleaner interface for me to see what is going on, and helps with requesting SSL certificates for your domains.</p>
<h1 id="heading-how-to-set-up-nginx-proxy-manager">How to set up Nginx Proxy Manager</h1>
<p>The tutorials I followed was <a target="_blank" href="https://www.youtube.com/watch?v=P3imFC7GSr0&amp;t=620s&amp;ab_channel=TheDigitalLife">Nginx Proxy Manager - How-To Installation and Configuration - YouTube</a>, and <a target="_blank" href="https://nginxproxymanager.com/setup/#running-on-raspberry-pi-arm-devices">Full Setup Instructions | Nginx Proxy Manager</a>. However they are not entirely just for Raspberry Pi and a Flask website, so I will elaborate further on my use case. </p>
<p>Before you start, you should go to Port Forwarding settings in your router that we mention in Part 1 to open up 81 or any open port.</p>
<p>Firstly, create a folder <code>proxy-manager</code> in any directory (I created under mnt), and create a <code>docker-compose.yml</code> file with the code below:</p>
<pre><code class="lang-YAML"><span class="hljs-attr">version:</span> <span class="hljs-string">"3"</span>                                                                                                                       <span class="hljs-attr">docker-compose.yml                                                                                                                                  version:</span> <span class="hljs-string">"3"</span>
<span class="hljs-attr">services:</span>
  <span class="hljs-attr">app:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">'jc21/nginx-proxy-manager:latest'</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">unless-stopped</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-comment"># These ports are in format &lt;host-port&gt;:&lt;container-port&gt;</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'80:80'</span> <span class="hljs-comment"># Public HTTP Port</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'443:443'</span> <span class="hljs-comment"># Public HTTPS Port</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'81:81'</span> <span class="hljs-comment"># Admin Web Port can be other ports if you want</span>
      <span class="hljs-comment"># Add any other Stream port you want to expose</span>
      <span class="hljs-comment"># - '21:21' # FTP</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">DB_MYSQL_HOST:</span> <span class="hljs-string">"db"</span>
      <span class="hljs-attr">DB_MYSQL_PORT:</span> <span class="hljs-number">3306</span>
      <span class="hljs-attr">DB_MYSQL_USER:</span> <span class="hljs-string">"npm"</span>
      <span class="hljs-attr">DB_MYSQL_PASSWORD:</span> <span class="hljs-string">"npm"</span>
      <span class="hljs-attr">DB_MYSQL_NAME:</span> <span class="hljs-string">"npm"</span>
      <span class="hljs-comment"># Uncomment this if IPv6 is not enabled on your host</span>
      <span class="hljs-comment"># DISABLE_IPV6: 'true'</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./data:/data</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./letsencrypt:/etc/letsencrypt</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">db</span>

  <span class="hljs-attr">db:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">'jc21/mariadb-aria:latest'</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">unless-stopped</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">MYSQL_ROOT_PASSWORD:</span> <span class="hljs-string">'npm'</span>
      <span class="hljs-attr">MYSQL_DATABASE:</span> <span class="hljs-string">'npm'</span>
      <span class="hljs-attr">MYSQL_USER:</span> <span class="hljs-string">'npm'</span>
      <span class="hljs-attr">MYSQL_PASSWORD:</span> <span class="hljs-string">'npm'</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./data/mysql:/var/lib/mysql</span>
</code></pre>
<p>Then you start up the docker image with</p>
<pre><code class="lang-yaml"><span class="hljs-string">docker-compose</span> <span class="hljs-string">up</span> <span class="hljs-string">-d</span>
</code></pre>
<p>You should then be able to access Nginx Proxy Manager from <code>&lt;private IP address&gt;:81</code> The default login information is </p>
<pre><code class="lang-bash">Email:    admin@example.com
Password: changeme
</code></pre>
<p>After that, we can go to SSL Certificates and Add SSL Certificate</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653493396632/ROTUe_mS7.png" alt="ssl.png" /></p>
<p>Then we go to Dashboard &gt; Proxy Host and add a proxy host. Under Details, we put in the private IP address of our RPi, and forward the port to the flask app’s port, which is 5000 in our case. Toggle block common exploits for security sake.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653493445037/hhrHLjYn9.png" alt="proxyhostdetails.png" /></p>
<p>Moving to SSL option:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653493494719/MnSRJ32k4.png" alt="proxyhostoptions.png" /></p>
<p>Select the SSL certificate that you have generated previously, and turn on force SSL. An SSL secured website looks less dubious and also perform better in SEO in Google.</p>
<p>And tadaa! You should have a working website that shows the contents of your Flask App.</p>
]]></content:encoded></item><item><title><![CDATA[Self Hosted Website on a Raspberry Pi (Part 2)]]></title><description><![CDATA[This is a three part tutorial that cites relevant tutorial guides that I found online that help me eventually achieve my final goal of self-hosting my personal website.

Part 1 - RPi Setup
Part 2 - Flask App with Docker and Portainer (here)
Part 3 - ...]]></description><link>https://blog.tingwei.fans/self-hosted-website-on-a-raspberry-pi-part-2</link><guid isPermaLink="true">https://blog.tingwei.fans/self-hosted-website-on-a-raspberry-pi-part-2</guid><category><![CDATA[Raspberry Pi]]></category><category><![CDATA[Flask Framework]]></category><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Fan Ting Wei]]></dc:creator><pubDate>Wed, 25 May 2022 11:13:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/jOqJbvo1P9g/upload/v1653475747959/SBJkCqXk46.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is a three part tutorial that cites relevant tutorial guides that I found online that help me eventually achieve my final goal of self-hosting my personal website.</p>
<ul>
<li><a target="_blank" href="https://blog.tingwei.fans/hosting-your-own-website-on-a-raspberry-pi-part-1">Part 1 - RPi Setup</a></li>
<li>Part 2 - Flask App with Docker and Portainer (here)</li>
<li><a target="_blank" href="https://blog.tingwei.fans/self-hosted-website-on-a-raspberry-pi-part-3">Part 3 - Domain Name Management with Cloudflare and Nginx Proxy Manager</a></li>
</ul>
<p>To add more content to my old personal website, I created more routes to different pages. However, considering that the future of this personal website will be where I experiment with different frameworks, not every side project I will be doing can be integrated with Flask. So I decided to take a leap of faith to experiment for Docker, which after countless hours of tutorials I am starting to realise the value of it.</p>
<h1 id="heading-why-docker">Why Docker?</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653476075306/NoEDu4idF.png" alt="dockervsvm.png" />
My old personal website was built directly into the VPS, which are essentially virtual machines. When I install packages directly into the OS such as tensorflows/sklearn/xgboost, they may run into conflicts in the version of packages that they are dependent on. Spinning up another VPS or Raspberry Pi is definitely too overkill, so one may consider isolating package installs through virtual environments (venv). </p>
<p>However, that only works if we are working with only Python. What if we want to set up a MYSQL or redis server to work with our Flask app? What if we need that the same MYSQL database but a different version with another programming language? That’s when things becomes complicated. Spinning up more virtual machine on a virtual machine is too expensive, and communication between these virtual machine is even harder.</p>
<p>That’s when Docker comes into play, and compartmentalise individual application based on their dependencies, but allow them to still “share” libraries with other applications without duplicating them. All the application will share the same host OS, without the extra layer of guest OS that is present in virtual machine set up.</p>
<h1 id="heading-installing-docker-and-portainer-optional">Installing Docker (and Portainer - optional)</h1>
<p>Now it’s time to install Docker! There is a simple script for that, just run:</p>
<pre><code class="lang-shell">$ curl -sSL https://get.docker.com | sh
$ sudo pip install docker-compose
$ docker ps
</code></pre>
<p>We also use docker-compose for multi-container Docker image, which requires python which we install previously. Run <code>docker ps</code> to check if docker is successfully installed. It should show you CONTAINER ID, IMAGE, COMMAND as output. </p>
<p>[Optional] However, I like having a GUI to have a better view of what is going on, which is why we are going to install Portainer, a docker image that help us manage our docker images 🙂</p>
<pre><code class="lang-shell">$ sudo docker run -d -p 9443:9443 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:linux-arm
</code></pre>
<p>To break down the command, <code>-p 9443:9443</code> expose the portainer web client on the port 9443, and <code>--restart=always</code> make sures that portainer is always running even if something went round. The last part we are installing the community edition of portainer for raspberry pi which is an ARM chip. Here you should go to Port Forwarding settings in your router that we mention in Part 1 to open up 9443 if you choose to install Portainer.</p>
<p>Once portainer is running on docker, you can access the web UI through <code>localhost:9443</code> or <code>public IP address:9443</code>. You would arrive here, and create an admin account accordingly</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653476283671/J_fJemwxs.png" alt="portainer.png" />
After setting up the admin account you should reach this website. At the home page, there are a few containers that are already running for me, but you should have no containers that are running if it’s your first setup. </p>
<h1 id="heading-creating-flask-app">Creating Flask App</h1>
<p>First thing first, we need to update and install all the dependencies we need in the RPi, by running the commands below:</p>
<pre><code class="lang-shell">$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get install python3-pip
$ sudo apt-get install python3-dev
$ sudo apt-get install python3-setuptools
$ sudo apt-get install python3-venv
$ sudo apt-get install build-essential libssl-dev libffi-dev
</code></pre>
<p>With that we can start by creating our barebone Flask App in any directories you want in RPi with the dependencies printed out in requirements.txt</p>
<pre><code class="lang-shell">$ mkdir testflask
$ cd testflask
$ python -m venv venv
$ source ./venv/bin/activate
(venv)$ pip install wheels
(venv)$ pip install flask
(venv)$ pip freeze &gt; requirements.txt
(venv)$ sudo nano app.py
</code></pre>
<pre><code class="lang-python"><span class="hljs-comment">#app.py</span>
<span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask
app = Flask(__name__)

<span class="hljs-meta">@app.route("/")</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span>():</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">"&lt;h1&gt;Hello World&lt;/h1&gt;"</span>

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    app.run(host=<span class="hljs-string">'0.0.0.0'</span>, port=<span class="hljs-string">'5000'</span>)
</code></pre>
<p>After creating the file, we can run it to ensure it works.</p>
<pre><code class="lang-shell">(venv)$ python app.py
 * Serving Flask app 'app' (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on all addresses (0.0.0.0)
   WARNING: This is a development server. Do not use it in a production deployment.
 * Running on http://127.0.0.1:5001
 * Running on http://192.168.10.51:5001 (Press CTRL+C to quit)
(venv)$ deactivate
</code></pre>
<p>With this, we know our flask app is functioning, but this is only for development purposes. Now let’s deactivate and add some files to Dockerise our flask app. Create two files <code>Dockerfile</code> and <code>docker-compose.yaml</code> with the code below.</p>
<h3 id="heading-dockerfile">Dockerfile</h3>
<pre><code>FROM python:<span class="hljs-number">3.11</span>.0a6-alpine3<span class="hljs-number">.15</span>
WORKDIR <span class="hljs-operator">/</span>code
COPY requirements.txt <span class="hljs-operator">/</span>code
RUN pip install <span class="hljs-operator">-</span>r requirements.txt <span class="hljs-operator">-</span><span class="hljs-operator">-</span>no<span class="hljs-operator">-</span>cache<span class="hljs-operator">-</span>dir
COPY . /code
CMD python app.py
</code></pre><h3 id="heading-docker-composeyml">docker-compose.yml</h3>
<pre><code>services:
    web:
        build: .
        ports:
            <span class="hljs-operator">-</span> <span class="hljs-string">"5000:5000"</span>
        volumes:
            <span class="hljs-operator">-</span> .:<span class="hljs-operator">/</span>code
</code></pre><p>Docker compose uses the Dockerfile if you add the build command to your project’s docker-compose.yml. Your Docker workflow should be to build a suitable Dockerfile for each image you wish to create, then use compose to assemble the images using the build command. The docker-compose also determines which port is exposed, so if you are already using port 5000 for something, do change it to any other open port. You can also see that as long your application can run on Docker, even if it is a React App with Java Backend, you can still set it up this way to be hosted.</p>
<p>Now, you can access your website if you go to :5000. However, this is not the end, we will still need to link it up with your domain name and setup basic security for your server and website, which will be explained in the next part.</p>
<h1 id="heading-ps">P.S.</h1>
<p>Due to firewall settings which will be set up in the next part of the tutorial, you may need to pip install with trusted tags as seen from below if you are creating another flask app to be hosted</p>
<pre><code>pip install --trusted-host=[<span class="hljs-string">pypi.org</span>](<span class="hljs-link">http://pypi.org/</span>) --trusted-host=[<span class="hljs-string">files.pythonhosted.org</span>](<span class="hljs-link">http://files.pythonhosted.org/</span>) flask
</code></pre>]]></content:encoded></item><item><title><![CDATA[Self Hosted Website on a Raspberry Pi (Part 1)]]></title><description><![CDATA[Today, I bought myself my first Raspberry Pi 4B 8GB, for a slightly steep price of 229 dollars on Shopee. Why did I buy it? Firstly, I have been paying USD5 a month (As of last month, the price went up to USD6) for a puny 1GB RAM 25GB SSD Linux virtu...]]></description><link>https://blog.tingwei.fans/self-hosted-website-on-a-raspberry-pi-part-1</link><guid isPermaLink="true">https://blog.tingwei.fans/self-hosted-website-on-a-raspberry-pi-part-1</guid><category><![CDATA[Raspberry Pi]]></category><dc:creator><![CDATA[Fan Ting Wei]]></dc:creator><pubDate>Wed, 25 May 2022 07:19:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/gQyTxSrctvw/upload/v1653462678756/biHzIXNAC.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today, I bought myself my first Raspberry Pi 4B 8GB, for a slightly steep price of 229 dollars on Shopee. Why did I buy it? Firstly, I have been paying USD5 a month (As of last month, the price went up to USD6) for a puny 1GB RAM 25GB SSD Linux virtual private server (VPS) on Digital Ocean and it hosted my personal website for now. For a VPS with 8GB ram, I would have to fork out USD40 a month, so I thought why not just get a Raspberry Pi to host it? I could also use it as a NAS server to store my photos and movies as well by plugging in my hard disk on it, and if one day it outgrow my needs, I could just make a cluster of Raspberry Pi! And do many more things with it! (which I suspect might take awhile to even get there)</p>
<p>This is a three part tutorial that cites relevant tutorial guides that I found online that help me eventually achieve my final goal of self-hosting my personal website.</p>
<ul>
<li>Part 1 - RPi Setup (here)</li>
<li><a target="_blank" href="https://blog.tingwei.fans/hosting-your-own-website-on-a-raspberry-pi-part-2">Part 2 - Flask App with Docker and Portainer</a></li>
<li><a target="_blank" href="https://blog.tingwei.fans/self-hosted-website-on-a-raspberry-pi-part-3">Part 3 - Domain Name Management with Cloudflare and Nginx Proxy Manager</a></li>
</ul>
<h1 id="heading-hardware-setup">Hardware setup</h1>
<p>The setup was really simple, you take out the motherboard, plug in the 32GB SD card with the Raspberry Pi OS preinstalled by the seller. With the power, monitor, mouse and keyboard plugged into it, all we need to do is the switch it on to boot it up. It can connect to the internet either by WIFI or LAN, but I chose to connect it via WIFI because I don’t want to disconnect it accidentally if I knock it over.</p>
<h1 id="heading-setting-up-for-ssh-connections-for-local-access">Setting up for SSH connections for Local Access</h1>
<p>While you can use the Raspberry Pi like a desktop with a monitor, keyboard and a mouse, most of the time you would rather just access it via SSH like a VPS from whichever device we are more comfortable working on. Firstly, we need to enable the SSH, in my case through the OS GUI. </p>
<p>Once your device boots up:</p>
<ol>
<li>Click on the <strong>raspberry logo</strong> at the top-left corner.</li>
<li>Select <strong>Preferences</strong> &gt; <strong>Raspberry Pi Configuration</strong>.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653462795490/iDyhEpb0x.png" alt="raspi-config.png" /></p>
<ol>
<li>Navigate to the <strong>Interfaces</strong> tab in the configuration window.</li>
<li><strong>Enable SSH</strong> in the second line.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653462853935/BJjJMc6BO.png" alt="sshconfig.png" /></p>
<p>There are other ways to enable SSH without the GUI as well from the guide here: <a target="_blank" href="https://phoenixnap.com/kb/enable-ssh-raspberry-pi">How to Enable SSH on Raspberry Pi [Definitive Guide] (phoenixnap.com)</a></p>
<p>Now you can access your Raspberry Pi (RPi) on devices that are connected on the same wifi network, by typing.</p>
<pre><code>pi@raspberrypi<span class="hljs-symbol">:~</span>$ ssh pi@raspberry
</code></pre><p>If you have changed the name of the RPi, you can still access it by looking for the private IP address of your RPi in the network by typing ifconfig. The IP address is under eth0 if you connected your RPi via LAN cable, or wlan0 if the RPi is connected via Wifi. It’s good to keep this private IP address in mind because many other frameworks are easily accessible from the internal IP address via different ports. </p>
<pre><code>pi@raspberrypi:<span class="hljs-operator">~</span>$ ifconfig
pi@raspberrypi:<span class="hljs-operator">~</span>$ 
eth0: flags<span class="hljs-operator">=</span><span class="hljs-number">4099</span><span class="hljs-operator">&lt;</span>UP,BROADCAST,MULTICAST<span class="hljs-operator">&gt;</span>  mtu <span class="hljs-number">1500</span>
        <span class="hljs-literal">ether</span> <span class="hljs-operator">&lt;</span>IP here<span class="hljs-operator">&gt;</span>  txqueuelen <span class="hljs-number">1000</span>  (Ethernet)
        ...
wlan0: flags<span class="hljs-operator">=</span><span class="hljs-number">4163</span><span class="hljs-operator">&lt;</span>UP,BROADCAST,RUNNING,MULTICAST<span class="hljs-operator">&gt;</span>  mtu <span class="hljs-number">1500</span>
        inet <span class="hljs-operator">&lt;</span>IP here<span class="hljs-operator">&gt;</span>  netmask <span class="hljs-number">255.255</span><span class="hljs-number">.255</span><span class="hljs-number">.0</span>  broadcast <span class="hljs-number">192.168</span><span class="hljs-number">.18</span><span class="hljs-number">.255</span>
        ...
</code></pre><h1 id="heading-port-forwarding-for-remote-access">Port Forwarding for Remote Access</h1>
<p>However, this doesn’t allow us to access our RPi when you leave your home network. In order to expose port 22 in your router so for you to SSH into the RPi while you are outside of your home network, you need to set up port forwarding that is dependent on your router. At the same time, we are going to open port 80 and 443 to eventually allow HTTP and HTTPS request to our RPi later in the tutorial</p>
<p>I am using a Nokia Beacon, so there is a convenient phone app that allows me to adjust such settings. You can follow this guide for other routers - <a target="_blank" href="https://www.hellotech.com/guide/for/how-to-port-forward#:~:text=To%20forward%20ports%20on%20your%20router%2C%20log%20into%20your%20router,you%20might%20have%20to%20upgrade.">How to Port Forward on Your Router : HelloTech How</a></p>
<p>From my Nokia Beacon App:</p>
<ol>
<li>Settings &gt; General &gt; Advanced &gt; Port Forwarding.</li>
<li>Set External and Internal Port as 22 (Default Port for SSH), do it for 80 and 443 as well.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653462974837/FKCn7eM7R.jpg" alt="portforwarding2.jpg" /></p>
<p>After you set that up, you can find out your public IPv4 address by going to What Is My IP Address - See Your Public Address - IPv4 &amp; IPv6</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653463015751/d8OeEV2wk.png" alt="whatsmyip.png" /></p>
<pre><code>C:\ ssh pi@<span class="hljs-operator">&lt;</span>YOUR IP ADDRESS<span class="hljs-operator">&gt;</span>
</code></pre><p>It’s good to remember this <strong>public IP address</strong> because many other frameworks are easily accessible from the public IP address via different ports when you are working with your RPi remotely.</p>
<p>This public IP address will not change unless you reset your router, so you could either request an static IP address from your provider (which is next to impossible in Singapore) or you could employ Cloudflare DDNS API to update your public IP address when it does change. That is a topic for another day.</p>
<p>So in this part of the tutorial we managed to do the below:</p>
<p>1) Set up SSH for the RPi for you to access it <strong>on the same home network</strong> via its <strong>private IP address</strong>
2) Set up Port Forwarding for you to access your RPi <strong>remotely</strong>, via its <strong>public IP address</strong></p>
]]></content:encoded></item></channel></rss>