Building a Complete CI/CD Pipeline on VPS with GitLab Runner and Docker
Introduction to CI/CD on VPS Infrastructure
Continuous Integration and Continuous Deployment (CI/CD) has become an essential practice for modern software development teams. While cloud-based CI/CD solutions offer convenience, deploying your own CI/CD pipeline on a Virtual Private Server (VPS) provides greater control, cost efficiency, and customization options. This guide demonstrates how to build a complete CI/CD pipeline using GitLab Runner and Docker on your VPS infrastructure.
By the end of this tutorial, you will have a fully functional automated deployment system that builds, tests, and deploys your applications whenever code changes are pushed to your repository.
Prerequisites and System Requirements
Before beginning the implementation, ensure your environment meets the following requirements:
- VPS Specifications: Minimum 2 CPU cores, 4GB RAM, and 20GB storage
- Operating System: Ubuntu 20.04 LTS or later (other Linux distributions work with minor adjustments)
- Root Access: SSH access with sudo privileges
- GitLab Account: Either GitLab.com account or self-hosted GitLab instance
- Domain Name: Optional but recommended for production deployments
These specifications support small to medium-sized projects. Scale resources accordingly based on your application's complexity and build frequency.
Installing Docker on Your VPS
Docker serves as the foundation for our CI/CD pipeline, providing containerization for consistent build and deployment environments. Follow these steps to install Docker:
First, update your system packages and install required dependencies:
sudo apt-get update && sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common
Add Docker's official GPG key and repository, then install Docker Engine:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
After installation, enable Docker to start on boot and add your user to the docker group to run commands without sudo:
sudo systemctl enable docker && sudo usermod -aG docker $USER
Verify the installation by running docker --version and docker run hello-world. A successful test confirms Docker is properly configured.
Setting Up GitLab Runner
GitLab Runner is the agent that executes CI/CD jobs defined in your repository. Installing and configuring the runner properly is crucial for pipeline functionality.
Installation Process
Download and install the official GitLab Runner package:
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash
sudo apt-get install gitlab-runner
The GitLab Runner service will start automatically after installation. Verify its status with sudo systemctl status gitlab-runner.
Registering the Runner
To connect your runner to GitLab, you need the registration token from your GitLab project or group settings. Navigate to Settings > CI/CD > Runners in your GitLab interface to obtain this token.
Execute the registration command:
sudo gitlab-runner register
During registration, you will be prompted for:
- GitLab instance URL: https://gitlab.com or your self-hosted URL
- Registration token: From your project settings
- Runner description: A meaningful name like "production-vps-runner"
- Tags: Labels for targeting specific runners (e.g., "docker", "production")
- Executor: Select "docker"
- Default Docker image: Specify a base image like "alpine:latest"
After successful registration, your runner appears in the GitLab interface and is ready to execute jobs.
Configuring the CI/CD Pipeline
The pipeline configuration resides in a .gitlab-ci.yml file at your repository root. This YAML file defines stages, jobs, and deployment logic.
Basic Pipeline Structure
A typical pipeline includes multiple stages executed sequentially:
- Build Stage: Compiles code and creates Docker images
- Test Stage: Runs automated tests and quality checks
- Deploy Stage: Pushes changes to production or staging environments
Here is a foundational pipeline configuration:
stages:
- build
- test
- deploy
Each stage contains one or more jobs that execute specific tasks. Jobs within the same stage run in parallel, while stages execute sequentially.
Building Docker Images
The build stage typically creates a Docker image containing your application. Configure Docker-in-Docker (DinD) to build images within the pipeline:
build:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker build -t myapp:$CI_COMMIT_SHA .
- docker tag myapp:$CI_COMMIT_SHA myapp:latest
This configuration uses GitLab's built-in variables like $CI_COMMIT_SHA to tag images with commit identifiers, enabling version tracking.
Implementing Automated Testing
The test stage validates code quality before deployment. Include unit tests, integration tests, and security scans:
test:
stage: test
image: node:16
script:
- npm install
- npm run test
- npm run lint
Failed tests automatically stop the pipeline, preventing broken code from reaching production.
Deployment Strategies and Best Practices
The deployment stage transfers your application to the target environment. Implement proper security measures and rollback capabilities.
SSH-Based Deployment
For VPS deployments, SSH provides secure access to your server. Store SSH private keys as GitLab CI/CD variables (masked and protected) and configure deployment jobs:
deploy:
stage: deploy
script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- ssh-add <(echo "$SSH_PRIVATE_KEY")
- ssh -o StrictHostKeyChecking=no user@your-vps "cd /app && docker-compose pull && docker-compose up -d"
Environment-Specific Deployments
Use GitLab environments to manage staging and production deployments separately. Configure deployment rules based on branches:
- Staging: Automatically deploy from develop branch
- Production: Manually deploy from main branch with approval gates
This approach prevents accidental production deployments and maintains environment isolation.
Security Considerations
Security must be prioritized throughout your CI/CD pipeline implementation:
- Secret Management: Never commit credentials to repositories. Use GitLab CI/CD variables with masking enabled
- Image Scanning: Integrate container vulnerability scanning tools like Trivy or Clair
- Network Isolation: Configure firewall rules to restrict runner access to necessary services only
- Regular Updates: Keep GitLab Runner, Docker, and base images updated with security patches
- Access Control: Implement role-based access control (RBAC) for pipeline modifications
Additionally, enable Docker Content Trust to ensure image integrity and implement image signing for production deployments.
Monitoring and Maintenance
A production CI/CD pipeline requires ongoing monitoring and maintenance. Implement these practices:
- Pipeline Metrics: Track build times, success rates, and failure patterns
- Resource Monitoring: Monitor VPS CPU, memory, and disk usage to prevent resource exhaustion
- Log Aggregation: Centralize logs from runners and deployed applications for troubleshooting
- Backup Strategy: Regularly backup runner configurations and deployment scripts
- Performance Optimization: Use caching strategies to reduce build times and bandwidth consumption
GitLab provides built-in analytics for pipeline performance. Review these metrics regularly to identify optimization opportunities.
Conclusion
Implementing a complete CI/CD pipeline on your VPS with GitLab Runner and Docker provides a robust, cost-effective solution for automated software delivery. This setup offers the flexibility to customize every aspect of your deployment process while maintaining full control over your infrastructure.
The combination of GitLab's powerful CI/CD features and Docker's containerization capabilities creates a professional-grade deployment system suitable for production environments. As your needs grow, this foundation scales easily by adding more runners, implementing advanced deployment strategies, or integrating additional tools into your pipeline.
Start with the basic configuration outlined in this guide, then gradually enhance your pipeline with advanced features like blue-green deployments, canary releases, or automated rollback mechanisms. The investment in a well-designed CI/CD pipeline pays dividends through faster delivery cycles, improved code quality, and reduced deployment risks.
