Automatic publication of website changes through Github Actions
This blog post is more than 19 months old and may be out of date.
Hello! My name is Krista and I’m a Zone front-end software developer. Like many other professional developers, I can sometimes feel that the code I write at work is not enough, so I consider writing code outside of work for my own self-development. I want to share my experience with a useful GitHub feature that should facilitate the development process of a website.

On my “self-improvement journeys”, I have often found myself thinking how annoying it is to enter the server management, then file management and then finally open the right folder and replace the old files with new ones after every little change on my website. It would be so much easier and more convenient if some kind of automation would do it for me. For example, right after the push to the main branch of the version control system. So I decided it was finally time to take my first informed step into the world of CI/CD and share my discoveries with you.
After some research, I came across a functionality called Actions on GitHub, the main purpose of which is to automate the workflow of developers. The capabilities of Actions include building code, running tests, as well as delivering code from the GitHub repository (hereinafter repo) directly to the server.
Does it require any additional fees?
For private repos, free GitHub users are allowed 2,000 minutes of GitHub Actions workflow implementation time per month.
In the case of public repos, implementation of workflows is always free, regardless of minutes.
In addition, it should be taken into account that the free package has up to 500 MB of data, which is calculated on the basis of all repos that belong to your user account.
If the limits above are exceeded, the billing on the basis of the GitHub’s price list starts from the moment the limits are exceeded.
So let’s begin then…
1. Preparations
First, let’s look at what you need:
- A server in Zone (all packages we offer are suitable).
This article uses the Starter package of our web hosting as an example - GitHub account
- Ability to use the command line (for SSH key pair generation)
And let’s make some preparations:
1. Prepare the domain where you want your website to be deployed automatically in the future. In my example, I created a subdomain called “actions” for the server
2. Create and set up a GitHub repo in your website code
OR fork one of my repos for testing:
- a page that doesn’t need building (HTML + CSS)
- a page that needs building (Vite + Vue)
2. SSH key pair and settings in Zone
Generate an SSH key pair. This can be done from the command line or using tools like PuTTY.
Log on to My Zone and navigate to your server’s SSH settings. Add your public key and enable access from anywhere using the Access from
dropdown.*
* This may have made you raise an eyebrow. I definitely recommend reading the last section of this blog article!
3. Adding secrets to your GitHub repo settings
Secrets are repo-specific – if you want to set up a code delivery workflow to the same server from several different repos, you need to add secrets to each repo separately, unless the repos are owned by an organisation.
Open your repo settings in GitHub and go to Secrets and variables > Actions
under the Security
section in the left menu.
(Please note! Click on the image to enlarge.)

In total, you need to add three secrets:
- HOST_SERVER_IP – you can find that on the SSH settings page in My Zone
- SSH_LOGIN_CREDS – copy the user name and IP address of your server from the SSH configuration page and separate them with an at sign (virtXXXX@IPaadress)
- SSH_PRIVATE_KEY – private key of the key pair generated previously
Please note! I recommend copying the necessary information into a separate text file, because if you make a typo somewhere when adding secrets, you can change the value of the secret, but the old value will not be displayed.
Copying to a separate text file is also useful if you want to create a code delivery workflow to the same server from several different repos (e.g. if you forked both repos linked above for testing)
4. Creating a workflow file
Next, navigate to the Actions
subpage from the repo‘s top menu and create a new workflow.

on the workflow file editing page, there is a very useful sidebar on the right side, where you can find an excerpt from the workflow documentation and the Actions Marketplace.

I have prepared two workflow files:
- the first one for a web page that does not need to be built, i.e. where files are simply copied from the repo to the server
- the second for a web page that needs to be built before copying the files to the server. In my example, it is a Vite + Vue page.
I recommend having the documentation on the workflow file editing page and the Marketplace on the side to familiarise yourself with the contents of the files and the Actions used.
First example – deploy
name: Deploy to Zone
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: production
url: https://YOURDOMAIN.EU
steps:
- name: Checkout to main
uses: actions/checkout@main
- name: Set up SSH
run: |
mkdir -p ~/.ssh/
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.HOST_SERVER_IP }} >> ~/.ssh/known_hosts
- name: Copy files to server
run: |
rsync -vrm ./* ${{ secrets.SSH_LOGIN_CREDS }}:/dataXX/virtXXXXX/domeenid/WWW.YOURDOMAIN.EU/actions
Code language: JavaScript (javascript)
Second example – build & deploy
name: Build & Deploy to Zone
on:
push:
branches:
- main
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout to main
uses: actions/checkout@main
- name: Set up Node
uses: actions/setup-node@v3.8.1
- name: Install dependencies
run: npm install
- name: Build project
run: npm run build
- name: Upload built files
uses: actions/upload-artifact@v3.1.3
with:
name: production-files
path: ./dist
deploy:
name: Deploy
runs-on: ubuntu-latest
needs: build
environment:
name: production
url: https://YOURDOMAIN.EU
steps:
- name: Checkout to main
uses: actions/checkout@main
- name: Download built files
uses: actions/download-artifact@v2.1.1
with:
name: production-files
path: ./dist
- name: Connect to server over SSH
run: |
mkdir -p ~/.ssh/
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.HOST_SERVER_IP }} >> ~/.ssh/known_hosts
- name: Deploy built site
run: |
rsync -vrm ./dist/ ${{ secrets.SSH_LOGIN_CREDS }}:/dataXX/virtXXXXX/domeenid/www.YOURDOMAIN.EU/actions
Code language: JavaScript (javascript)
Please note! Both codes have some keywords that you need to replace with the correct values:
- replace “YOURDOMAIN.EU” with your own domain name
- dataXX with your server partition (you can find it in My Zone in the system data of the server service)
- virtXXXXX with your own server’s system user name
- and be sure to review the destination file path on the rsync command line. (If you want to deploy directly to the main domain and you have not changed the root directory of the main domain, change the
/actions
folder to/htdocs
at the end of the line)
I also recommend using the Marketplace section on the workflow file editing page to check the versions of used Actions and change them to newer ones in the file if necessary.
Please note! The given rsync command does not delete the files in the destination directory, but overwrites the files with the identical name and extension! If you want the command to behave differently, check the documentation for the rsync command.
5. Commit and observe the game
When the workflow file is ready and adjusted according to your wishes, press the commit button and open Actions
from the top again.
You should see your first workflow run there.
As long as it hasn’t succeeded or failed yet, it will have an orange icon next to it. However, when the workflow run is complete, it should resemble the image below.

In the case of GitHub Actions, it’s convenient that when you click on the description of the workflow run, a page opens up where you can see precisely what error occurred when it failed.

By clicking on the failed job under Jobs on the left or in the main.yml box in the middle, it is possible to see at which step the error occurred with a more detailed error description.

If the workflow was successful, then by visiting your site you should be able to see it in the same state as it is in your Github repo’s main branch. And from now on, to deploy, all you have to do is write code and push (or merge a pull request) to your main branch.
Can it be any safer?
Is it possible to use a similar solution to deploy larger and mission-critical websites? It’s possible, but with more emphasis on security, this process has to be made much more complicated. The biggest concern of the solution I presented is that the whole process starts from outside the server, where you connect to the server via SSH, each time from a different IP address (we use GitHub-hosted runners).
Even if the private SSH key is stored as securely as possible, it is recommended to additionally implement an allowlist based on IP addresses or their ranges on the server. Unfortunately, GitHub has made this as difficult as possible for the average user.
GitHub-hosted runners use ranges of IP addresses from Microsoft Azure data centres that can be requested via API, but are changed weekly and there can be hundreds of them in use at once. (While doing some research for this blog article, I found that under the “actions” key in GitHub’s Meta endpoint, there are over 3720 IP address ranges with CIDR notation, between which there is apparently a weekly rotation).
Technically, we can remove IP addresses from the allowlist and add them via the API (not publicly documented), but doing so to hundreds of ranges of IP-addresses is probably already exceeding the limit of reasonable use.
Therefore, GitHub has also stated in their documentation that they do not recommend using IP address ranges of GitHub-hosted runners for the purpose of the allowlist, but to consider alternatives. For example, they also offer large runners with a static range of IP addresses, which are paid and only available to organisations or users of the GitHub Team / Enterprise Cloud packages. It is also possible to use self-hosted runners, for which GitHub does not charge an additional fee.
However, it is probably even better to think of a solution where the entire process takes place internally on the server, with Jenkins for example.
Post navigation
Popular posts

Why choose a .EU domain today?

Ecommerce SEO essentials: How to boost search visibility and drive sales

New at Zone: Varist – even stronger malware protection
