โš™๏ธ
Hosting

CI/CD with GitHub Actions: Automating Tests and Server Deployment

11.11.2025
โ† All articles

In modern software development, writing code is only half of the job. Reviewing that code, running it through tests, and deploying it to a server are equally important steps, yet performing all of them by hand is slow, tedious, and prone to mistakes. The CI/CD methodology emerged precisely to solve this problem, allowing teams to automate repetitive tasks and focus on what truly matters. In this article we will take a detailed look at what CI/CD is, why it has become essential for modern teams, and how to put it into practice using GitHub Actions.

What is CI/CD and why you need it

CI/CD is an abbreviation that combines two concepts: Continuous Integration and Continuous Delivery or Deployment. Continuous Integration means that every developer regularly, often several times a day, merges their changes into a shared repository, and each merge is automatically built and tested. This approach helps catch conflicts between different parts of the codebase at the earliest possible stage, before they have a chance to grow into a serious and expensive problem to untangle.

Continuous Delivery, on the other hand, is the process of automatically deploying successfully tested code into a production environment or onto a server. The greatest benefit of this process is that errors are discovered long before they ever reach the end user, while the code is still in the development pipeline. If you introduce a small change that accidentally breaks a feature, the automated tests will point it out immediately, and you will fix the issue in a matter of minutes rather than hours or days. As a result, the team moves faster, code quality improves, and the journey from a commit to the user becomes dramatically shorter.

What is GitHub Actions

GitHub Actions is an automation tool built directly into the GitHub platform. It allows you to run automated tasks in response to specific events in your repository, such as pushing code, opening a pull request, or hitting a defined schedule. The biggest advantage of GitHub Actions is that it lives in the very same place where your code already resides, so you do not have to configure or integrate separate third-party services to build your automation pipelines.

Each automation scenario is called a workflow and is described by a YAML file located in the .github/workflows folder of your repository. GitHub automatically reads these files and triggers them whenever the defined conditions are met. To execute your code, GitHub provides its own cloud servers, known as runners, which are virtual machines, so you never have to worry about maintaining your own infrastructure just to run your builds and tests.

Workflow structure: triggers, jobs, and steps

Understanding the structure of a workflow file is the key to mastering CI/CD. Every workflow consists of three main layers: triggers define when the workflow runs, jobs represent groups of work that can run in parallel or sequentially and independently from one another, and steps are the concrete commands executed in order within each job. The example below shows the simplest possible workflow structure:

name: CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm install

      - name: Run tests
        run: npm test

In this example the on section defines the triggers, meaning the workflow runs whenever code is pushed to the main branch or a pull request is opened. The build job under jobs runs on an Ubuntu server and consists of four steps: first the code is checked out, then the Node.js environment is prepared, after which dependencies are installed and finally the tests are executed.

Automating deployment to a server

Once the tests pass successfully, the next logical step is to deploy the code to a server. Most commonly this is done by connecting to the server over SSH, pulling the new code, and restarting the service. The example below shows a workflow that automatically deploys to a sayt.uz hosting server after the tests have passed:

name: Deploy

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Deploy to server
        uses: appleboy/ssh-action@v1.0.0
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            cd /var/www/myproject
            git pull origin main
            npm install --production
            pm2 restart app

Here the workflow runs every time code is pushed to the main branch, connects to the server via SSH, pulls the latest version of the code, installs the necessary dependencies, and restarts the application. This way, apart from writing code, you do not have to perform a single manual action, and your changes automatically reach the live server and become available to users right away.

Working with secrets

In the example above you have probably noticed entries like ${{ secrets.SSH_KEY }}. These are sensitive values stored through GitHub's secrets system. Server passwords, SSH keys, API tokens, or database connection strings should never be written directly into the code, because from a security standpoint that is extremely dangerous. Instead, you store them in the repository settings, under the Settings section, in the Secrets and variables subsection.

Such secrets are stored in encrypted form, and they can only be accessed while the workflow is running, and even in the logs they are automatically masked and replaced with asterisks. This approach ensures that your sensitive data does not sit in the repository in plain text and remains inaccessible to outsiders. For this reason, managing any password or key exclusively through secrets is considered the best practice and a fundamental security requirement.

Testing across multiple environments with a matrix

Quite often you need to make sure that your code works correctly across different language versions or different operating systems. Instead of writing a separate job for each version, you can use the matrix strategy. A matrix automatically multiplies a single job with different parameters, for example letting you test your code simultaneously across several versions of Node.js:

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18, 20, 22]
    steps:
      - uses: actions/checkout@v4
      - name: Setup Node ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm install
      - run: npm test

With this configuration GitHub Actions will automatically create three separate jobs, each running on a different version of Node.js. This gives you confidence that your code runs reliably across all the environments your broad audience uses, and it helps you catch incompatibilities between versions early, before they ever reach your users in production.

Real-world benefits and conclusion

The practical value of adopting CI/CD is immense. Thanks to automation, the team saves the hours that used to be spent on manual testing and deployment, the chance of human error drops sharply, and code quality stays consistently high. Most importantly, developers gain the ability to ship updates frequently and confidently, without fear of the deployment process, which noticeably accelerates the overall pace of product development.

GitHub Actions is one of the most convenient tools to begin this journey, since it sits in the same place as your code and requires no complex infrastructure to set up. If you are deploying your project to a sayt.uz hosting plan or server, you can adapt the deployment workflow shown above to your own needs and fully automate your entire delivery process. A CI/CD pipeline configured once will serve you for years and protect your team's most valuable resource, which is time itself.

Related articles

๐Ÿ’ฐ Hosting Price Comparison: Uzbek and International Providers ๐Ÿ“ก Server Monitoring Tools: Prometheus, Grafana, Datadog, and More ๐ŸŒ Edge Computing Hosting: Moving Compute Closer to Users ๐Ÿข Colocation Server: Placing Your Own Hardware in a Data Center
๐ŸŒ Language
๐Ÿ‡บ๐Ÿ‡ฟ O'zbek ๐Ÿ‡บ๐Ÿ‡ฟ ะŽะทะฑะตะบ ๐Ÿ‡ท๐Ÿ‡บ ะ ัƒััะบะธะน ๐Ÿ‡ฌ๐Ÿ‡ง English โœ“