<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: jtorbett23</title>
    <description>The latest articles on DEV Community by jtorbett23 (@jtorbett23).</description>
    <link>https://dev.to/jtorbett23</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F567342%2F94eb5cce-0d3e-4e5c-95bb-f469911b5309.png</url>
      <title>DEV Community: jtorbett23</title>
      <link>https://dev.to/jtorbett23</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jtorbett23"/>
    <language>en</language>
    <item>
      <title>Hosting a Godot Server on Oracle Cloud</title>
      <dc:creator>jtorbett23</dc:creator>
      <pubDate>Mon, 15 Dec 2025 10:42:15 +0000</pubDate>
      <link>https://dev.to/jtorbett23/hosting-a-godot-server-on-oracle-cloud-2p4g</link>
      <guid>https://dev.to/jtorbett23/hosting-a-godot-server-on-oracle-cloud-2p4g</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When working on multiplayer projects hosting a server is a key part of being able to share it with others.&lt;/p&gt;

&lt;p&gt;In this article we will cover how to host a Godot (4.5.1) Server on an Oracle cloud instance. For this we will cover:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Godot project&lt;/li&gt;
&lt;li&gt;Dockerising a Godot server&lt;/li&gt;
&lt;li&gt;Hosting a Godot server to an Oracle instance&lt;/li&gt;
&lt;li&gt;Setting up HTTPS with NGINX and letsencrypt&lt;/li&gt;
&lt;li&gt;Hosting your client on itch.io&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://godotengine.org" rel="noopener noreferrer"&gt;Godot&lt;/a&gt; 4.5.1&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.docker.com" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; and a Docker account&lt;/li&gt;
&lt;li&gt;An &lt;a href="https://www.oracle.com/uk/cloud/" rel="noopener noreferrer"&gt;Oracle cloud&lt;/a&gt; account

&lt;ul&gt;
&lt;li&gt;Oracle has websites for each region so pick your closest&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;An &lt;a href="https://itch.io" rel="noopener noreferrer"&gt;itch.io&lt;/a&gt; account&lt;/li&gt;

&lt;li&gt;A registered domain

&lt;ul&gt;
&lt;li&gt;This is only required if you want HTTPS&lt;/li&gt;
&lt;li&gt;Personally I use &lt;a href="https://www.godaddy.com" rel="noopener noreferrer"&gt;GoDaddy&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a id="section_1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Godot Project
&lt;/h2&gt;

&lt;p&gt;The Godot project that we will host is using Godot 4.5.1 in compatibility mode and Godot's &lt;a href="https://docs.godotengine.org/en/stable/tutorials/networking/high_level_multiplayer.html" rel="noopener noreferrer"&gt;high-level multiplayer&lt;/a&gt;, specifically the WebSocketServer. This is because in this tutorial we will be hosting our client on the web.&lt;/p&gt;

&lt;p&gt;As for what the project does, it allows players to join and leave a 2D space where they can move around with their connection ids shown above them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpcv5d0ggwjz4nbdp81rw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpcv5d0ggwjz4nbdp81rw.png" alt="Project Screeshot" width="800" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find the source code for the project &lt;a href="https://github.com/jtorbett23/godot-multiplayer-hosting-tutorial" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a id="section_2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Dockerising a Godot server
&lt;/h2&gt;

&lt;p&gt;When dockerising an application it is important to have a clear idea of the steps we would manually take to achieve the same effect. In our case we want to have to have a Linux based version of our game running as a Server.&lt;/p&gt;

&lt;p&gt;We can achieve this in the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Setup a Linux based system to run our Server&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM ubuntu:focal AS build

# Install dependenices to download Godot and templates
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y --no-install-recommends \
ca-certificates \
unzip \
wget 
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;For this we are using the smallest ubuntu docker image I could find and then install dependencies to download Godot. &lt;br&gt;
(If you know of a smaller one, let me know in the comments)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install Godot and the templates required to export projects for Linux&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ENV GODOT_VERSION="4.5.1"
# Download Godot and templates
RUN wget https://github.com/godotengine/godot/releases/download/${GODOT_VERSION}-stable/Godot_v${GODOT_VERSION}-stable_linux.x86_64.zip 
RUN wget https://github.com/godotengine/godot/releases/download/${GODOT_VERSION}-stable/Godot_v${GODOT_VERSION}-stable_export_templates.tpz 

# Place Godot files in required folders
RUN mkdir -p ~/.cache ~/.config/godot ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable \
&amp;amp;&amp;amp; unzip Godot_v${GODOT_VERSION}-stable_linux*.zip \
&amp;amp;&amp;amp; mv Godot_v${GODOT_VERSION}-stable_linux*64 /usr/local/bin/godot \
&amp;amp;&amp;amp; unzip Godot_v${GODOT_VERSION}-stable_export_templates.tpz \
&amp;amp;&amp;amp; mv templates/* ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable \
&amp;amp;&amp;amp; rm Godot_v${GODOT_VERSION}-stable_export_templates.tpz Godot_v${GODOT_VERSION}-stable_linux*.zip
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Here we use &lt;code&gt;wget&lt;/code&gt; to download Godot and the templates for exports from the &lt;a href="https://github.com/godotengine/godot/releases" rel="noopener noreferrer"&gt;Godot Github releases&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Export a version of our game for Linux&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Create a space to build the executable
RUN mkdir /godotbuildspace
WORKDIR /godotbuildspace

# Copy our Godot project into the container
COPY . . 
ARG EXECUTABLE_NAME
ENV EXPORT_NAME="LinuxServer"
ENV EXECUTABLE_NAME=$EXECUTABLE_NAME

# Export the project in debug or release mode
ENV EXPORT_MODE="debug" 
RUN godot --export-${EXPORT_MODE} ${EXPORT_NAME} ${EXECUTABLE_NAME} --headless
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;With Godot downloaded we can now export the project where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;EXECUTABLE_NAME&lt;/code&gt;: is the name of our final executable&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EXPORT_NAME&lt;/code&gt;: is the name of our Linux export in our &lt;code&gt;export_presets.cfg&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EXPORT_MODE&lt;/code&gt;: is the type of export release or debug. I recommend at this stage using &lt;code&gt;debug&lt;/code&gt; as this will allow you to see logs from &lt;code&gt;print()&lt;/code&gt; in your Godot code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This stage also requires you have setup a Linux export in your &lt;code&gt;export_presets.cfg&lt;/code&gt; file. The easiest way to do this is in your Godot editor go to &lt;code&gt;Project -&amp;gt; Export&lt;/code&gt; and then &lt;code&gt;Add... -&amp;gt; Linux&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2v0b762eop2ew0ki6yf3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2v0b762eop2ew0ki6yf3.png" alt="Linux Export Setup" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this export preset we are using &lt;code&gt;Options -&amp;gt; Embed PCK = true&lt;/code&gt; and &lt;code&gt;Resources -&amp;gt; Export Mode = Export as dedicated server&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the exported version as a Server&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Start a new stage to execute the exectuable as we do not need the Godot files anymore
FROM ubuntu:focal

ARG EXECUTABLE_NAME
ENV EXECUTABLE_NAME=$EXECUTABLE_NAME
COPY --from=build /godotbuildspace/ ./
EXPOSE 6069/tcp
EXPOSE 6069/udp
CMD ["sh", "-c", "./${EXECUTABLE_NAME} --headless -s"]
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Finally we run the created executable. For this tutorial:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Port &lt;code&gt;6069&lt;/code&gt; is used, as it is defined as the port the client will connect to &lt;a href="https://github.com/jtorbett23/godot-multiplayer-hosting-tutorial/blob/main/multiplayer_manager.gd#L3" rel="noopener noreferrer"&gt;here&lt;/a&gt;, and will require tcp and udp connections.&lt;/li&gt;
&lt;li&gt;We run with the flag &lt;code&gt;--headless&lt;/code&gt; as this runs Godot without visual elements, which the server will not require.&lt;/li&gt;
&lt;li&gt;We run with the flag &lt;code&gt;-s&lt;/code&gt; as this is how in this project we tell our Godot project to run as a server not a client, the code of this can be seen &lt;a href="https://github.com/jtorbett23/godot-multiplayer-hosting-tutorial/blob/main/game.gd#L6-L10" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now with this file we can build the container with &lt;code&gt;docker build -t mutliplayer-hosting-tutorial-server .&lt;/code&gt; and run it with &lt;code&gt;docker run -d -p 6069:6069 -p 6069:6069/udp mutliplayer-hosting-tutorial-server&lt;/code&gt;. However, if you do want to use this for local development I recommend using &lt;code&gt;docker-compose&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For later steps we will need to push the image to our docker account so we can pull it on the Oracle instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build -t &amp;lt;Your Docker Username&amp;gt;/mutliplayer-hosting-tutorial-server .
docker push &amp;lt;Your Docker Username&amp;gt;/mutliplayer-hosting-tutorial-server 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find the full Dockerfile &lt;a href="https://github.com/jtorbett23/godot-multiplayer-hosting-tutorial/blob/main/Dockerfile" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a id="section_3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Hosting a Godot server to an Oracle instance
&lt;/h2&gt;

&lt;p&gt;We are going to use Oracle for hosting our server as you can get two instances for free.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create the Oracle Instance&lt;/p&gt;

&lt;p&gt;Start by going to the instances section in Oracle Cloud and create a new instance.&lt;/p&gt;

&lt;p&gt;For this we are using &lt;code&gt;Canonical Ubuntu 20.04&lt;/code&gt; image and &lt;code&gt;VM.Standard.E2.1.Micro&lt;/code&gt; shape.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd2d76q5intqml7qsjqep.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd2d76q5intqml7qsjqep.png" alt="Oracle Image and Shape" width="800" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure to download your private and public key for SSH.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fodhkyg39vegae39huqbq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fodhkyg39vegae39huqbq.png" alt="Oracle SSH key" width="719" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can leave all the other settings as default.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Setup Port Connections&lt;/p&gt;

&lt;p&gt;Our server will be communicating on port 6069 on both tcp and udp, so we need to allow connections on those ports.&lt;/p&gt;

&lt;p&gt;For this we need to change our subnet rules to allow this traffic. These subnet rules can be changed for your instance by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Selecting your instance&lt;/li&gt;
&lt;li&gt;Going to the networking tab and selecting the subnet&lt;/li&gt;
&lt;li&gt;Then add a new one:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy45t829500udyhpy0aok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy45t829500udyhpy0aok.png" alt="Oracle Subnet rules" width="800" height="54"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Download docker and run container&lt;/p&gt;

&lt;p&gt;To connect to our server for our local machine we will use the ssh key we downloaded earlier. This can be done with the following command in your terminal:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh -i &amp;lt;Path to your .key file&amp;gt; ubuntu@&amp;lt;Public IP of your instance&amp;gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Now on the machine we need to first update the package installer &lt;code&gt;apt&lt;/code&gt; and then install docker.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt update
sudo apt install docker.io
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Then download your published image and run it.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo docker pull &amp;lt;Your Docker username&amp;gt;/mutliplayer-hosting-tutorial-server
sudo docker run -d -p 6069:6069 -p 6069:6069/udp &amp;lt;Your Docker username&amp;gt;/mutliplayer-hosting-tutorial-server
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Now we can connect from our local client by updating &lt;code&gt;127.0.0.1&lt;/code&gt; to the public IP address of your server &lt;a href="https://github.com/jtorbett23/godot-multiplayer-hosting-tutorial/blob/main/multiplayer_manager.gd#L4" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a id="section_4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Setting up HTTPS with NGINX and letsencrypt
&lt;/h2&gt;

&lt;p&gt;We want to setup HTTPS as the current HTTP setup will work for local development builds of your Godot client if we want to host our client on itch.io we will require a secure connection to communicate with our server. &lt;/p&gt;

&lt;p&gt;Currently we are using a websocket connection with &lt;code&gt;ws://&lt;/code&gt; and we will need to use a secure websocket connection with &lt;code&gt;wss://&lt;/code&gt; from a trusted domain meaning we cannot self sign the connection.&lt;/p&gt;

&lt;p&gt;To get that secured signed connection we will be using &lt;a href="https://letsencrypt.org" rel="noopener noreferrer"&gt;letsencrypt&lt;/a&gt; which requires a registered domain to sign our connection as secure.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Security list updates&lt;/p&gt;

&lt;p&gt;With the HTTPS setup we will not be using connection on a the port 6069 directly but instead traffic from the standard HTTPS port 443 will be forwarded to it. This means we can update our security list for the subnet removing 6069 and adding 80 and 443:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs6ellcabte1tys91na2w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs6ellcabte1tys91na2w.png" alt="Updated HTTPS Security List" width="800" height="51"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Point your domain to IP of server&lt;/p&gt;

&lt;p&gt;For this tutorial we will be getting our domain from GoDaddy, you can pick up a cheap domain for year for cheap if you do not mind what the name of that domain will be.&lt;/p&gt;

&lt;p&gt;With a domain we need to now setup a A Record, this points our domain to the Public IP address of our server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcnjy48jh9je05exhy7is.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcnjy48jh9je05exhy7is.png" alt="GoDaddy A Record" width="800" height="159"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This record will point traffic for the sub-domain &lt;code&gt;multiplayer-tutorial&lt;/code&gt; for the domain to the public IP address of the server, such that &lt;code&gt;multiplayer-tutorial.&amp;lt;Domain name&amp;gt;&lt;/code&gt; will go to the servers public IP address.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a NGINX to server&lt;/p&gt;

&lt;p&gt;Now we have our domain setup we need to handle requests coming to that domain and make sure any HTTP connections get redirection to HTTPS.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install nginx
# Verify that nginx is now running
sudo service nginx status
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Now we want to update our nginx config to send any traffic at the domain we defined to our Godot server. You can find the nginx config file at &lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt;, in this tutorial we will just directly edit this file.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Open the config
sudo nano /etc/nginx/nginx.conf
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;We will then add the following &lt;code&gt;server&lt;/code&gt; block to the &lt;code&gt;http&lt;/code&gt; block:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http {
  server {
    server_name  &amp;lt;Your subdomain&amp;gt;.&amp;lt;Your domain&amp;gt;;
    location / {
    # redirect all HTTP traffic to localhost:6069
    proxy_pass http://localhost:6069;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # WebSocket support
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    }
 }
...
}
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This will forward traffic for your domain setup to port 6069 and make use of websockets for that connection. We can can then validate the changes with the following:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Check the file syntax
sudo nginx -t
# Restart the server
service nginx restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add lets encrypt&lt;/p&gt;

&lt;p&gt;For this we will need to add a firewall rule to allow letsencrypt to verify our server:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo iptables -L --line-numbers
#OUTPUT
Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination
1    ACCEPT     all  --  anywhere             anywhere             state RELATED,ESTABLISHED
2    ACCEPT     icmp --  anywhere             anywhere
3    ACCEPT     all  --  anywhere             anywhere
4    ACCEPT     tcp  --  anywhere             anywhere             state NEW tcp dpt:ssh
5    REJECT     all  --  anywhere             anywhere             reject-with icmp-host-prohibited
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;We need to add in a line before the REJECT for traffic on port 80 for letsencrypt and port 443 to allow traffic on a secure connection:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo iptables -I INPUT 5 -m state --state NEW -p tcp --dport 80 -j ACCEPT
sudo iptables -I INPUT 5 -m state --state NEW -p tcp --dport 443 -j ACCEPT
# Save the changes
sudo netfilter-persistent save
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Now we can run certbot, the a cli used for letsencrypt, to verify our domain and update our nginx config.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install certbot python3-certbot-nginx
sudo certbot --nginx -d &amp;lt;Your Subdomain&amp;gt;.&amp;lt;Your Domain&amp;gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Make sure to select option 2 to redirect traffic to HTTPS.&lt;/p&gt;

&lt;p&gt;This should result in 2 server blocks &lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    server_name  &amp;lt;Your subdomain&amp;gt;.&amp;lt;Your Domain&amp;gt;;
    location / {
    # redirect all HTTP traffic to localhost:6069
    proxy_pass http://localhost:6069;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # WebSocket support
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
   }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/&amp;lt;Your subdomain&amp;gt;.&amp;lt;Your Domain&amp;gt;/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/&amp;lt;Your subdomain&amp;gt;.&amp;lt;Your Domain&amp;gt;/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

...
server {
    if ($host = &amp;lt;Your subdomain&amp;gt;.&amp;lt;Your Domain&amp;gt;) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    server_name  &amp;lt;Your subdomain&amp;gt;.&amp;lt;Your Domain&amp;gt;;
    listen 80;
    return 404; # managed by Certbot
}
...
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Note: If certbot fails here, retry after an hour as most DNS records (like the A record we setup) normally take an hour (but can take up to 48 hours) to propagate.&lt;/p&gt;

&lt;p&gt;With this we now have an HTTPS connection we can use for websockets for our domain. Our connection will only stay certified for ~45 days through letscencrypt and will require refreshing our certification, for this I recommend setting up a &lt;a href="https://en.wikipedia.org/wiki/Cron" rel="noopener noreferrer"&gt;CRON&lt;/a&gt; job on your instance to automate this.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a id="section_5"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Hosting your client on itch.io
&lt;/h2&gt;

&lt;p&gt;With our server now running with a secure connection itch.io will allow the client we upload to it to connect. &lt;/p&gt;

&lt;p&gt;To upload our client to the web we first need to export it in Godot as a HTML project. For this make sure to name export the &lt;code&gt;.html&lt;/code&gt; file as &lt;code&gt;index.html&lt;/code&gt; as itch.io requires this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwvo4rudli1t3fpajvhya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwvo4rudli1t3fpajvhya.png" alt=" " width="800" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will create a folder with your exported project, which then needs to be compressed to a zip folder.&lt;/p&gt;

&lt;p&gt;With your compressed client folder, create a project on itch.io and upload it. Make sure to select your project as HTML.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvk83txwqkcg1u0vdrtv6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvk83txwqkcg1u0vdrtv6.png" alt=" " width="577" height="78"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally you can run your web clients, in two tabs and should see two players.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6k2bi4qgzsx5ybha3qa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6k2bi4qgzsx5ybha3qa.png" alt=" " width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;With this you should be able to host your Godot servers for only the cost of a domain name. This is great for hobbyist projects, testing and game jams. I hope this allows you to create more multiplayer projects.&lt;/p&gt;

</description>
      <category>itch</category>
      <category>oracle</category>
      <category>godot</category>
      <category>docker</category>
    </item>
    <item>
      <title>Render &amp; Github Actions</title>
      <dc:creator>jtorbett23</dc:creator>
      <pubDate>Wed, 26 Mar 2025 17:24:12 +0000</pubDate>
      <link>https://dev.to/jtorbett23/render-github-actions-2b92</link>
      <guid>https://dev.to/jtorbett23/render-github-actions-2b92</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;In this tutorial we will cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hosting an API on render.&lt;/li&gt;
&lt;li&gt;Using Github actions to run our tests prior to deploying to render.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prerequisites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com" rel="noopener noreferrer"&gt;Github&lt;/a&gt; account &amp;amp; how to push code to it&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://render.com" rel="noopener noreferrer"&gt;Render&lt;/a&gt; account (this can be a free account)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pipenv.pypa.io/en/latest/installation.html" rel="noopener noreferrer"&gt;Pipenv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The API being hosted
&lt;/h3&gt;

&lt;p&gt;For this tutorial I will be using a example &lt;a href="https://fastapi.tiangolo.com" rel="noopener noreferrer"&gt;fastapi&lt;/a&gt; API written with Python 3.13.0. &lt;/p&gt;

&lt;p&gt;Feel free to fork from the &lt;a href="https://github.com/jtorbett23/fastapi-render-tutorial/tree/tutorial-start" rel="noopener noreferrer"&gt;&lt;code&gt;tutorial-start&lt;/code&gt;&lt;/a&gt; branch if you wish to follow along.&lt;/p&gt;

&lt;p&gt;If you wish to use your own API the general steps will be the same but make sure that both Render and Github Actions have support for it.&lt;/p&gt;

&lt;p&gt;The project is a simple API that outputs &lt;code&gt;Hello World&lt;/code&gt; and has 3 commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pipenv run dev&lt;/code&gt; to run the API locally for development&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pipenv run prod&lt;/code&gt; to run the API in production context e.g when hosted on Render&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pipenv run test&lt;/code&gt; to run the tests for the API.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Hosting an API on render
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;On your Render dashboard create a new &lt;code&gt;Web Service&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select your newly created Github project. You will need to setup a connection to Github for Render via this &lt;a href="https://render.com/docs/github" rel="noopener noreferrer"&gt;guide&lt;/a&gt;.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update the configuration for your programming language version to match the version of your project. &lt;/p&gt;

&lt;p&gt;For Python we need to set the &lt;code&gt;PYTHON_VERSION&lt;/code&gt; to&lt;code&gt;3.13.0&lt;/code&gt; using an Environment Variable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update the &lt;code&gt;Build Command&lt;/code&gt; which will install our project dependencies on Render.&lt;/p&gt;

&lt;p&gt;For this project it will be &lt;code&gt;pipenv install&lt;/code&gt;. We do not required the dev dependencies here as tests will be run on Github Actions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update the &lt;code&gt;Start Command&lt;/code&gt; which will be run to start up our API on render.&lt;/p&gt;

&lt;p&gt;For this project it will be &lt;code&gt;pipenv run prod&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After the deployment is complete visit url for the website to see &lt;code&gt;Hello World&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With the current setup as we push new changes to Github it will trigger deployments to Render automatically via &lt;a href="-%20https://render.com/docs/deploy-hooks"&gt;deploy hooks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Try updating the message to &lt;code&gt;{"message": "Hello World Again!"}&lt;/code&gt; in &lt;code&gt;main.py&lt;/code&gt; and push to Github to see this  in effect.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Github actions to run our tests prior to deploying to render
&lt;/h3&gt;

&lt;p&gt;Now we have our tests we can run use Github Actions to run our tests and only deploy to render if they pass.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Disable the current auto-deploy on Render by setting "Auto Deploy" to "No" in the settings of your Render project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add Github Actions &lt;a href="https://github.com/jtorbett23/fastapi-render-tutorial/blob/main/.github/workflows/ci.yml" rel="noopener noreferrer"&gt;script&lt;/a&gt; to the project at &lt;code&gt;github/workflows/ci.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This script has two stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;test: this will install the dependencies for running your tests and run them&lt;/li&gt;
&lt;li&gt;deploy: this will deploy your code (if on main) to render&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fln8vzginjdrkkk3jhol2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fln8vzginjdrkkk3jhol2.png" alt="Github Actions yml" width="800" height="910"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should see your tests execute fine but it fails to deploy. This is because we still need to add the &lt;code&gt;RENDER_DEPLOY_HOOK_URL&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhe3g6r6kjkj3ew8c5dfi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhe3g6r6kjkj3ew8c5dfi.png" alt="Failed Github Actions" width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Add &lt;code&gt;RENDER_DEPLOY_HOOK_URL&lt;/code&gt; to the secrets for Github actions&lt;/p&gt;

&lt;p&gt;In the setting of your Render project find the &lt;code&gt;Deploy Hook&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Copy the value of this as a new repository secret for Github Actions, this is found at Settings -&amp;gt; Secrets and variables -&amp;gt; Actions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Try updating the message to &lt;code&gt;{"message": "Hello World Again with Github Actions!"}&lt;/code&gt; but before pushing make sure your tests pass locally via &lt;code&gt;pipenv run test&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr3dkrz2naa44up7ydocs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr3dkrz2naa44up7ydocs.png" alt="Passing Github Actions" width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Congratulations you have now successfully setup a CI/CD pipeline via Github Actions deploying to Render.&lt;/p&gt;

&lt;p&gt;Completed &lt;a href="https://github.com/jtorbett23/fastapi-render-tutorial" rel="noopener noreferrer"&gt;Github Project&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://fastapi-render-tutorial-67z2.onrender.com" rel="noopener noreferrer"&gt;Live url&lt;/a&gt; - As this is hosted via the free version of Render it may require a minute to startup if it has been idle. &lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>webdev</category>
      <category>githubactions</category>
      <category>fastapi</category>
    </item>
    <item>
      <title>React + Gitlab Pages</title>
      <dc:creator>jtorbett23</dc:creator>
      <pubDate>Tue, 26 Jan 2021 17:52:40 +0000</pubDate>
      <link>https://dev.to/jtorbett23/react-gitlab-pages-42l6</link>
      <guid>https://dev.to/jtorbett23/react-gitlab-pages-42l6</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this tutorial we will be showing you how to deploy a react application with a Gitlab deployment pipeline to Gitlab pages.&lt;/p&gt;

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

&lt;p&gt;For this tutorial you will need to have the following setup &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Github Account - &lt;a href="https://github.com/join" rel="noopener noreferrer"&gt;https://github.com/join&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Gitlab Account - &lt;a href="https://gitlab.com/users/sign_up" rel="noopener noreferrer"&gt;https://gitlab.com/users/sign_up&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;git - &lt;a href="https://git-scm.com/downloads" rel="noopener noreferrer"&gt;https://git-scm.com/downloads&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;node and npm - &lt;a href="https://nodejs.org/en/download/" rel="noopener noreferrer"&gt;https://nodejs.org/en/download/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check git, node &amp;amp; npm have installed properly with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git --version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node --version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm --version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating our react app
&lt;/h2&gt;

&lt;p&gt;In a directory of your choice create a react app with the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-react-app gitlab-pages-react-example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Feel free to change gitlab-pages-react-example to whatever name you desire)&lt;/p&gt;

&lt;p&gt;Enter your new react project folder&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd gitlab-pages-react-example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use &lt;code&gt;npm start&lt;/code&gt; to check your project runs properly and you should see the following at &lt;code&gt;http://localhost:3000&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flmoy00pakkklbnf705yc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flmoy00pakkklbnf705yc.png" alt="Screenshot 2021-01-26 at 15.50.13" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Upload your react app to Github
&lt;/h2&gt;

&lt;p&gt;Create an empty public repository on Github. Then to your local react project and enter these commands to push your code to Github&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git remote add origin https://github.com/jtorbett23/gitlab-pages-react-example.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(You will need to replace &lt;a href="https://github.com/jtorbett23/gitlab-pages-react-example.git" rel="noopener noreferrer"&gt;https://github.com/jtorbett23/gitlab-pages-react-example.git&lt;/a&gt; with the link to your repository)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git push -u origin master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should now see your react application on your Github repository:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvf5iyqaomhtucj8zmvcz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvf5iyqaomhtucj8zmvcz.png" alt="Screenshot 2021-01-26 at 15.54.49" width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Linking Gitlab and Github
&lt;/h2&gt;

&lt;p&gt;Login to your Gitlab account and create a new project choosing "Run CI/CD for external repository" selecting the repo we created earlier&lt;/p&gt;

&lt;p&gt;Once created open your project and go to Settings &amp;gt; General &amp;gt; Visibility, project features, permissions. Then check that Gitlab pages is allowed&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqwgef4y30qi6h0769jel.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqwgef4y30qi6h0769jel.png" alt="Screenshot 2021-01-26 at 13.54.43" width="800" height="181"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating our deployment pipeline
&lt;/h2&gt;

&lt;p&gt;For gitlab to create a pipeline to deploy code it requires a &lt;br&gt;
&lt;code&gt;.gitlab-ci.yml&lt;/code&gt; file at the root level of project. &lt;br&gt;
(Read more gitlab yaml here - &lt;a href="https://docs.gitlab.com/ee/ci/yaml/" rel="noopener noreferrer"&gt;https://docs.gitlab.com/ee/ci/yaml/&lt;/a&gt;) &lt;/p&gt;

&lt;p&gt;Here is the &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; we will be starting with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;image: node

pages:
  stage: deploy
  cache:
    paths:
      - node_modules/
  script:
    - npm install
    - npm run build
    - rm -rf public
    - cp build/index.html build/404.html
    - mv build public
  artifacts:
    paths:
      - public
  only:
    - master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Images
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;image: node&lt;/code&gt; defines node as the type of image that docker will use, allowing use to access npm easily.&lt;/p&gt;

&lt;p&gt;Note: if you require a specific node version that can be specified by adding :NODE-VERSION e.g &lt;code&gt;image: node:10.16.3&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Stages
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pages:
  stage: deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setups a stage for our pipeline, in which we can execute various scripts. For Gitlab pages we need to name this stage "pages" so it deploys content to the correct place.&lt;/p&gt;

&lt;h4&gt;
  
  
  Caching
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cache:
    paths:
      - node_modules/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caches our node_modules so we don't need to download dependencies every time we run our pipeline.&lt;/p&gt;

&lt;h4&gt;
  
  
  Scripts
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;scripts:&lt;/code&gt; allows use to run scripts through a terminal&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm install&lt;/code&gt; first installs/updates our dependencies &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm run build&lt;/code&gt; the builds our project into a build folder&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rm -rf public&lt;/code&gt; will remove the public folder as we need to use the namespace public to help Gitlab pages recognise our site content&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cp build/index.html build/404.html&lt;/code&gt; as react is a single page app we set the 404 page to a copy of our index.html to handle errors through index.html&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mv build public&lt;/code&gt; copy the built project from build to public to allow Gitlab pages to recognise it once deployed&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Artifacts
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;artifacts:
    paths:
      - public
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Artifacts are the output of a pipeline stage and we output our public folder holding our built site.&lt;/p&gt;

&lt;h4&gt;
  
  
  Restricting pipeline stages
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  only:
    - master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;only&lt;/code&gt; lets us set what code branches of project will run this pipeline stage, it is set to master so we don't push development code to our site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying to Gitlab pages
&lt;/h2&gt;

&lt;p&gt;Once your &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; has been pushed to your Github repo Gitlab will sync these changes and run a pipeline based off it.&lt;/p&gt;

&lt;p&gt;Your root folder should look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-public
-src
-.gitignore
-.gitlab-ci.yml
-package-lock.json
-package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Gitlab navigate to CI/CD &amp;gt; Pipelines and you should see a pipeline has been triggered. Once your pipeline is completed it should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdrt2lq4f14h60wj52n0s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdrt2lq4f14h60wj52n0s.png" alt="Screenshot 2021-01-26 at 17.39.51" width="800" height="76"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After your pipeline is complete you will be able to view your site by navigating to Settings &amp;gt; Pages and clicking the url under "Access pages"&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2yjdjbuxxilftlxvb85q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2yjdjbuxxilftlxvb85q.png" alt="Screenshot 2021-01-26 at 16.45.50" width="800" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your page should look like this:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzgj3akrhwdtfcoqcwb3k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzgj3akrhwdtfcoqcwb3k.png" alt="Screenshot 2021-01-26 at 16.44.09" width="800" height="442"&gt;&lt;/a&gt;&lt;br&gt;
Note: we are now have the padlock so are using https&lt;/p&gt;

&lt;p&gt;If you instead just get a white page, you will need to edit your &lt;code&gt;package.json&lt;/code&gt; and add the "homepage" attribute. For my project my url is &lt;code&gt;https://jtorbett23.gitlab.io/gitlab-pages-react-example/&lt;/code&gt;, so i will need to set my homepage as "gitlab-pages-react-example".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "homepage": "gitlab-pages-react-example",
  "name": "gitlab-pages-react-example",
  "version": "0.1.0",
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will need to wait for new pipeline to successfully run before seeing the changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying a change from our local machine
&lt;/h2&gt;

&lt;p&gt;Now let's make a change to locally to see if updates our site automatically.&lt;/p&gt;

&lt;p&gt;Change the text in &lt;code&gt;src/App.js&lt;/code&gt; from&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;p&amp;gt;
  Edit &amp;lt;code&amp;gt;src/App.js&amp;lt;/code&amp;gt; and save to reload.
&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to whatever you would like e.g&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;p&amp;gt;
  Deployment pipeline is working :)
&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Push these changes and it should trigger your deployment pipeline. After it has finished you should see your changes on Gitlab Pages&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F74k3r387k35azrh6okdi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F74k3r387k35azrh6okdi.png" alt="Screenshot 2021-01-26 at 16.57.45" width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Improving our pipeline
&lt;/h2&gt;

&lt;p&gt;Currently our pipeline only consists of a single step which means when we are developing further down the line it will be hard to know why our pipeline is failing.&lt;/p&gt;

&lt;p&gt;So we are going to separate our pipeline into three stages: build, test and deploy.&lt;/p&gt;
&lt;h4&gt;
  
  
  Build
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;build:
  stage: build
  cache:
    paths:
      - node_modules/
  script:
    - npm install
    - npm run build
  artifacts:
    paths:
      - build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here we install and cache our dependencies and then build the project outputted as an artifact to be accessible by other stages.&lt;/p&gt;
&lt;h4&gt;
  
  
  Test
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test:
  stage: test
  cache:
    paths:
      - node_modules/
    policy: pull
  script:
    - npm run test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here we use the cached dependencies through the &lt;code&gt;policy: pull&lt;/code&gt; to run our test scripts.&lt;/p&gt;

&lt;p&gt;For this stage we will also need to update test script in the &lt;code&gt;package.json&lt;/code&gt; to make sure it finds all our test scripts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
 "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --watchAll=false",
    "eject": "react-scripts eject"
  }
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is done by adding the option &lt;code&gt;--watchAll=false&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Deployment
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pages:
  stage: deploy
  dependencies: 
    - build
  script:
    - rm -rf public
    - cp build/index.html build/404.html
    - mv build public
  artifacts:
    paths:
      - public
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we take the built project from the build stage's artifact and then deploy the code as normal.&lt;/p&gt;

&lt;p&gt;Here is the final &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;image: node

build:
  stage: build
  cache:
    paths:
      - node_modules/
  script:
    - npm install
    - npm run build
  artifacts:
    paths:
      - build

test:
  stage: test
  cache:
    paths:
      - node_modules/
    policy: pull
  script:
    - npm run test

pages:
  stage: deploy
  dependencies: 
    - build
  script:
    - rm -rf public
    - cp build/index.html build/404.html
    - mv build public
  artifacts:
    paths:
      - public
  only:
    - master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;You have now learned how to deploy a react application from Github to Gitlab pages using Gitlab CI/CD pipeline.&lt;/p&gt;

&lt;p&gt;Here are the links for the completed project for reference:&lt;br&gt;
Github - &lt;a href="https://github.com/jtorbett23/gitlab-pages-react-example" rel="noopener noreferrer"&gt;https://github.com/jtorbett23/gitlab-pages-react-example&lt;/a&gt;&lt;br&gt;
Gitlab - &lt;a href="https://gitlab.com/jtorbett23/gitlab-pages-react-example" rel="noopener noreferrer"&gt;https://gitlab.com/jtorbett23/gitlab-pages-react-example&lt;/a&gt;&lt;br&gt;
Gitlab Pages url - &lt;a href="https://jtorbett23.gitlab.io/gitlab-pages-react-example/" rel="noopener noreferrer"&gt;https://jtorbett23.gitlab.io/gitlab-pages-react-example/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
    </item>
  </channel>
</rss>
