This article will describe how to build a website using the Astro Framework, NGINX and host it cheaply on a Vultr Virtual Private Server. We’ll also demonstrate how to implement continuous delivery using Github webhooks and a simple CGI script
The intent is to demonstrate the core tenets of Continuous Delivery without obscuring them with the complexity of fully-fledged CI/CD tooling like Github Actions or Jenkins.
This article assumes you are familiar with the following technologies:
apt
Create your Astro site using the command below
npx create astro@latest
git remote add origin \<url of your GitHub repo\>
git add --all
git commit -m "Initial Commit"
Don’t push your code just yet, we’re going to set up our Virtual Private Server (VPS) first and configure it to run a build of our Astro site on every push
We will be using SSH to access our VPS, to do that we need to create an SSH key Vultr will install on the server, so we can connect to it remotely.
ssh-keygen
C:\\Users\\\<username\>\.ssh
on Unix it would be ~/.ssh
id_rsa.pub
fileWe’re now ready to create our VPS, use the details below to configure a Cloud server to host our new Astro website.
Category | Option |
---|---|
Server Type | Cloud Compute |
CPU & Storage Technology | intel - Regular Performance |
Server Location | Choose somewhere cheap |
Server Image | Ubuntu 22.04 LTS |
Server Size | 10GB SSD 1 vCPU 0.5GB Memory 0.5TB Bandwidth |
Automatic Backups | Off |
SSH Key | Add your Public SSH Key |
shell ssh root@\<VPS IP Address\>
apt update
tail -f /var/log/unattended-upgrade/unattended-upgrades.log
When the log file indicates the upgrade is complete you can move on to the next step 6. Install the required software using the following command
apt install nginx python3-certbot fcgiwrapper
Update your DNS entries to point to the IP of the new VPS server
The service you bought your domain through usually provides a control panel to do this, a quick google of <domain provider> DNS control panel will usually turn up the right documentation
Take note of the fully qualified host name of the VPS in the Product Dashboard
mkdir /var/www/\<domain name\>
echo hello > /var/www/\<domain name\>/index.html # Create a simple test file to serve that will allow us to verify our configuration
chown www-data:www-data /var/www\<domain name\>
/etc/nginx/sites-available
# /etc/nginx/sites-available/\<domain name\>.conf
server {
listen 80; # Listen on port 80 on your IPv4 Address
list [::]:80; # Listen on port 80 on your IPv6 Address
server_name \<domain name\> www.<\domain name\>;
index index.html;
root /var/www/\<domain name\>; # Make sure it matches the directory you created above
location / {
try_files $uri $uri/ =404; # Make the server respond with 404 - Not found if requested file isn't present
}
}
nginx -t
/etc/nginx/sites-enabled
folderln -s /etc/nginx/sites-available/\<domain name\>.conf /etc/nginx/sites-enabled
systemctl restart nginx
curl -H "Host: \<domain name\>" http://localhost/index.html
It should respond with hello
Hat’s off to Let’s Encrypt, they could not have made securing a website any easier. To enable SSL for your new virtual host simply run:
certbot --nginx
The above command will prompt you to select the domain names you want to request certificates for and then update your NGINX configuration to enable SSL and restart the NGINX service using systemctl.
If you want to review the changes open your NGINX configuration file in /etc/nginx/sites-available
We will use FastCGI to serve the webhook that is going to be the foundation of our Continuous Delivery process.
/etc/nginx/sites-available
/var/www/webhooks
location /webhooks/ {
gzip off;
root /var/www;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME /var/www$fastcgi_script_name;
}
We’re almost there! We’re going to use 2 scripts to handle building our website and connect them to a GitHub webhook. The first script is responsible for ensuring there is only one build in progress at any point in time. The second script contains our build logic.
We need some scratch space to perform our builds, create a new directory and change the ownership so the NGINX user has read-write access to it
mkdir /var/build
chown www-data:www-data /var/build
Our build script needs to be able to clone our website’s code. For security, we will create a dedicated SSH key.
⚠️ Do not use the same key you created to access your VPS server
ssh-keygen
chown www-data:www-data /var/build/ida_rsa*
cat /var/build/ida_rsa.pub
The launch script ensures that the build process completes regardless of whether the caller of the webhook (GitHUb) hangs up or not. It also ensures that only 1 build at a time is processed.
Create a new file called: /var/www/webhooks/build.cgi
and copy the following contents into it
#!/bin/bash
pushd /var/www/webhooks
if [ ! -z $(pgrep -F /tmp/build.pid) ]
then
echo "Status: 409"
echo ""
echo "<html><head><title>Build in Progress</title></head><body><h1>Build Already in Progress</body></html>"
echo ""
echo ""
exit 0
else
nohup /var/www/webhooks/doBuild.sh 2>&1 > /dev/null &!
echo $! > /tmp/build.pid
echo "Status: 202"
echo "\r\n"
echo "Build Started"
echo ""
echo ""
fi
exit 0
The build script uses NVM (Node Version Manager) to install NodeJS, Git to clone the repository and NPM to install the required libraries and frameworks and build the site
Create a new file called: /var/www/webhooks/doBuild.sh
and copy the following contents into it
export HOME=/var/build
if [ ! -d /var/build/nvm ]; then
mkdir /var/build/nvm
fi
export NVM_DIR=/var/build/nvm && wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.4/install.sh | bash 2>&1 | tee /var/build/build.log
export PATH=/var/build/nvm/bin:$PATH
source $NVM_DIR/nvm.sh
nvm install 18 2>&1 | tee --append /var/build/build.log
nvm use 18 2>&1 | tee --append /var/build/build.log
pushd /var/build
if [ -d /var/build/ee ]; then
rm -rf ee
fi
# Make sure you correctly refer to your new SSH Key in the next command, we disable strict host key checking since our NGINX user doesn't have a HOME directory
git -c "core.sshCommand=ssh -i **/var/build/id_rsa** -F /dev/null -o StrictHostKeyChecking=no" clone git@github.com:\<github user\>/\<github repository\>.git 2>&1 | tee --append /var/build/build.log
pushd ee
npm ci 2>&1 | tee --append /var/build/build.log
npm run build 2>&1 2>&1 | tee --append /var/build/build.log
rm -rf /var/www/\<domain name\>/*
cp -rf dist/* /var/www/\<domain name\> 2>&1 | tee --append /var/build/build.log
Please make sure you replace all instances of \<domain name\>
with the correct directory
chown www-data:www-data /var/webhooks/*
chmod +x /var/webhooks/*
You’ve already checked in your code to GitHub, so we can verify everything is working by calling the
/var/www/webhooks/build.cgi
script. To ensure there are no permission issues when this is running normally we must
run it as the www-data user via the su command
su www-data -c "/var/www/webhooks/build.cgi"
If everything goes to plan, the code should be checked out to the /var/build
folder, NodeJS will be installed and
your site will be built and copied to the root folder served by NGINX for that domain. You can look at the build logs
using the following command:
less /var/build/build.log
This will highlight any errors that may have occurred whilst building the code. If the build was successful you should be able to access your new site at your domain name: https://<domain name>
All that is left to do now is connect GitHub to our build script.
build.cgi
webhook https://\<domain name\>/webhooks/build.cgi
It’s time to verify everything is working together to continuously deliver your site to your new VPS. On your local machine make a minor but noticeable change to the App.tsx file and commit it. Don’t push it to your GitHub repo just yet.
To verify the webhook is being called we’re going to tail
the logs on the server to observe the webhook invocation
and any errors.
tail -f /var/log/nginx/\<domain name\>.*.log /var/build/build.log # monitor the log files and print any new content to the console.
You can now push your changes to the repository, a couple seconds later you should see the output in the log files update
as GitHub invokes your build script. The build.log
file will update with the output from the build process and should
terminate with a success message.
Visit your domain in a new browser window and you will see the change you made.
You have just created a continuous delivery pipeline using a cheap and reliable VPS