{"id":14239,"date":"2026-02-18T09:42:04","date_gmt":"2026-02-18T04:12:04","guid":{"rendered":"https:\/\/www.youstable.com\/blog\/?p=14239"},"modified":"2026-02-18T09:43:53","modified_gmt":"2026-02-18T04:13:53","slug":"create-ci-cd-on-linux-server","status":"publish","type":"post","link":"https:\/\/www.youstable.com\/blog\/create-ci-cd-on-linux-server","title":{"rendered":"How to Create CI\/CD on Linux Server with Docker and Git"},"content":{"rendered":"\n<p><strong>To create CI\/CD on a Linux server<\/strong>, provision a secure host, connect your Git repository, install a CI runner or orchestrator (Jenkins, GitHub Actions Runner, or GitLab Runner), define pipeline jobs or YAML to build, test, and package your app, then deploy to staging\/production via SSH or containers, with secrets management, rollback, and monitoring.<\/p>\n\n\n\n<p>In this guide, you\u2019ll learn how to create CI\/CD on a Linux server using proven DevOps practices. We\u2019ll cover multiple approaches Jenkins, GitHub Actions self hosted runners, GitLab Runner, and a lightweight Bash pipeline, so you can pick what fits your stack and budget.<\/p>\n\n\n\n<p>I\u2019ll share real world steps, commands, and security tips from 12+ years building pipelines for web hosting and cloud environments.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"what-is-ci-cd-on-a-linux-server\">What Is CI\/CD on a Linux Server?<\/h2>\n\n\n\n<div class=\"wp-block-media-text has-media-on-the-right is-stacked-on-mobile\"><div class=\"wp-block-media-text__content\">\n<p>CI\/CD (Continuous Integration\/Continuous Delivery) automates how you build, test, and deploy applications. On a Linux server, that means using a runner or orchestrator to pull your code on changes, run tests, build artifacts or containers, and deploy reliably to your environment. <\/p>\n<\/div><figure class=\"wp-block-media-text__media\"><img loading=\"lazy\" decoding=\"async\" width=\"1168\" height=\"784\" src=\"https:\/\/www.youstable.com\/blog\/wp-content\/uploads\/2025\/12\/What-Is-CICD-on-a-Linux-Server-2.png\" alt=\"What Is CI\/CD on a Linux Server?\n\" class=\"wp-image-14612 size-full\" srcset=\"https:\/\/www.youstable.com\/blog\/wp-content\/uploads\/2025\/12\/What-Is-CICD-on-a-Linux-Server-2.png 1168w, https:\/\/www.youstable.com\/blog\/wp-content\/uploads\/2025\/12\/What-Is-CICD-on-a-Linux-Server-2-150x101.png 150w\" sizes=\"auto, (max-width: 1168px) 100vw, 1168px\" \/><\/figure><\/div>\n\n\n\n<p>Core tools include Git, Docker, Jenkins, GitHub Actions, GitLab CI, and Nginx.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"who-is-this-for-and-what-youll-build\">Who Is This For and What You\u2019ll Build<\/h2>\n\n\n\n<p>This tutorial is ideal for developers, sysadmins, and small teams who want a secure, maintainable <a href=\"https:\/\/www.youstable.com\/blog\/what-is-ci-cd-on-linux-server\/\">CI\/CD pipeline<\/a>. You\u2019ll set up an Ubuntu\/Debian based server, add a CI agent, configure pipelines, and deploy a containerized web app behind Nginx with rollback. We\u2019ll also cover secrets, firewalls, logs, and monitoring.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"prerequisites-and-recommended-stack\">Prerequisites and Recommended Stack<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Linux server (Ubuntu 22.04+ or Debian 12 recommended)<\/li>\n\n\n\n<li>Root or sudo access, <a href=\"https:\/\/www.youstable.com\/blog\/how-to-add-ssh-keys-to-github-account\/\">SSH keys<\/a><\/li>\n\n\n\n<li>Git repository (GitHub, GitLab, Bitbucket)<\/li>\n\n\n\n<li>Docker and Docker Compose (or language runtime such as Node.js\/Python\/Java)<\/li>\n\n\n\n<li>Domain and DNS (optional but recommended for HTTPS)<\/li>\n<\/ul>\n\n\n\n<p><strong>Hosting tip:<\/strong> A VPS or cloud instance with 2\u20134 vCPU, 4\u20138 GB RAM suits most small CI\/CD workloads. If you prefer managed assistance, <strong><a href=\"https:\/\/www.youstable.com\/vps-hosting\/\">YouStable\u2019s VPS hosting<\/a><\/strong> and managed server plans make it easy to deploy CI\/CD with pre hardened images, snapshots, and 24\u00d77 support.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"step-1-baseline-server-setup-security-first\">Step 1: Baseline Server Setup (Security First)<\/h2>\n\n\n\n<p>Harden the server before you install CI tools. Use a non root user, SSH keys, a firewall, and essential packages.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Create a non-root user\nsudo adduser ci\nsudo usermod -aG sudo,docker ci\n\n# SSH hardening (on your local machine)\nssh-copy-id ci@your_server_ip\n\n# Basic packages and updates\nsudo apt update &amp;&amp; sudo apt -y upgrade\nsudo apt -y install git curl ufw <a href=\"https:\/\/www.youstable.com\/blog\/what-is-fail2ban-on-linux-server\">fail2ban<\/a> ca-certificates gnupg lsb-release\n\n# Enable firewall\nsudo ufw allow OpenSSH\nsudo ufw allow 80\/tcp\nsudo ufw allow 443\/tcp\nsudo ufw --force enable\n\n# Install Docker (official repo)\nsudo install -m 0755 -d \/etc\/apt\/keyrings\ncurl -fsSL https:\/\/download.docker.com\/linux\/ubuntu\/gpg | sudo gpg --dearmor -o \/etc\/apt\/keyrings\/docker.gpg\necho \\\n  \"deb &#91;arch=$(dpkg --print-architecture) signed-by=\/etc\/apt\/keyrings\/docker.gpg] \\\n  https:\/\/download.docker.com\/linux\/ubuntu $(. \/etc\/os-release &amp;&amp; echo $VERSION_CODENAME) stable\" | \\\n  sudo tee \/etc\/apt\/sources.list.d\/docker.list &gt; \/dev\/null\nsudo apt update\nsudo apt -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin\n\n# Allow ci user to run docker without sudo (re-login required)\nsudo usermod -aG docker ci<\/code><\/pre>\n\n\n\n<p>Optional: Install Nginx to reverse proxy your app and terminate TLS with <a href=\"https:\/\/www.youstable.com\/blog\/what-is-lets-encrypt-on-linux-server\">Let\u2019s Encrypt<\/a>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt -y install nginx\nsudo ufw allow \"Nginx Full\"\n\n# Example minimal reverse proxy (adjust server_name and upstream)\nsudo bash -c 'cat &gt;\/etc\/nginx\/sites-available\/app.conf' &lt;&lt;EOF\nserver {\n    listen 80;\n    server_name app.example.com;\n\n    location \/ {\n        proxy_pass http:\/\/127.0.0.1:8080;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n    }\n}\nEOF\nsudo ln -s \/etc\/nginx\/sites-available\/app.conf \/etc\/nginx\/sites-enabled\/app.conf\nsudo nginx -t &amp;&amp; sudo systemctl reload nginx<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"step-2-choose-your-ci-cd-architecture\">Step 2: Choose Your CI\/CD Architecture<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>GitHub Actions self hosted runner: <\/strong>Tight GitHub integration, YAML pipelines, fast for mono repos.<\/li>\n\n\n\n<li><strong>GitLab Runner:<\/strong> Works with GitLab CI\/CD using shell, Docker, or Kubernetes executors.<\/li>\n\n\n\n<li><strong>Jenkins:<\/strong> Highly extensible, plugin rich, great for complex, multi repo orchestration.<\/li>\n\n\n\n<li><strong>Lightweight Bash: <\/strong>Small teams can script build\/test\/deploy triggered by webhooks or cron.<\/li>\n<\/ul>\n\n\n\n<p>Pick one primary path below. You can mix and match (e.g., Actions for CI and Ansible for CD) as you scale.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"option-a-github-actions-self-hosted-runner\">Option A: GitHub Actions Self Hosted Runner<\/h3>\n\n\n\n<p>Run workflows on your own Linux server for full control and cache locality. Register the runner in your GitHub repo settings &gt; Actions &gt; Runners.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># As ci user\n<a href=\"https:\/\/www.youstable.com\/blog\/mkdir-command-in-linux\">mkdir<\/a> -p ~\/actions-runner &amp;&amp; cd ~\/actions-runner\n# Download the latest runner from GitHub (replace version\/URL from GitHub UI)\ncurl -o actions-runner.tar.gz -L https:\/\/github.com\/actions\/runner\/releases\/download\/vX.Y.Z\/actions-runner-linux-x64-X.Y.Z.tar.gz\ntar xzf actions-runner.tar.gz\n\n# Configure (use the token from GitHub Runner setup)\n.\/config.sh --url https:\/\/github.com\/your-org\/your-repo --token YOUR_TOKEN --labels self-hosted,linux,prod\n\n# Install as a service\nsudo .\/svc.sh install\nsudo .\/svc.sh start\nsudo systemctl status actions.runner.*<\/code><\/pre>\n\n\n\n<p>Example GitHub Actions workflow to build, test, Dockerize, and deploy with Docker Compose locally on the server:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>name: CI\/CD\non:\n  push:\n    branches: &#91; \"main\" ]\njobs:\n  build-test-deploy:\n    runs-on: self-hosted\n    steps:\n      - uses: actions\/checkout@v4\n\n      - name: Set up Node.js\n        uses: actions\/setup-node@v4\n        with:\n          node-version: '20'\n\n      - name: Install deps &amp; test\n        run: |\n          npm ci\n          npm test --if-present\n\n      - name: Build Docker image\n        run: |\n          docker build -t registry.example.com\/myapp:${GITHUB_SHA::7} .\n          docker tag registry.example.com\/myapp:${GITHUB_SHA::7} registry.example.com\/myapp:latest\n\n      - name: Push image (if using private registry)\n        env:\n          REGISTRY_USER: ${{ secrets.REGISTRY_USER }}\n          REGISTRY_PASS: ${{ secrets.REGISTRY_PASS }}\n        run: |\n          echo \"$REGISTRY_PASS\" | docker login registry.example.com -u \"$REGISTRY_USER\" --password-stdin\n          docker push registry.example.com\/myapp:${GITHUB_SHA::7}\n          docker push registry.example.com\/myapp:latest\n\n      - name: Deploy with Compose\n        run: |\n          docker compose pull || true\n          docker compose up -d --remove-orphans\n          docker image prune -f<\/code><\/pre>\n\n\n\n<p><strong>Create a minimal docker-compose.yml in your repo:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>services:\n  app:\n    image: registry.example.com\/myapp:latest\n    restart: unless-stopped\n    ports:\n      - \"8080:8080\"\n    env_file:\n      - .env<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"option-b-gitlab-runner-on-linux\">Option B: GitLab Runner on Linux<\/h3>\n\n\n\n<p>Use GitLab CI\/CD with a shell or Docker executor. Register your runner with a project or group token.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -L https:\/\/packages.gitlab.com\/install\/repositories\/runner\/gitlab-runner\/script.deb.sh | sudo bash\nsudo apt -y install gitlab-runner\n\n# Register runner (shell executor for simplicity)\nsudo gitlab-runner register \\\n  --url https:\/\/gitlab.com\/ \\\n  --registration-token YOUR_TOKEN \\\n  --executor shell \\\n  --description \"linux-shell-runner\"<\/code><\/pre>\n\n\n\n<p><strong>Sample .gitlab-ci.yml:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>stages: &#91;test, build, deploy]\n\nvariables:\n  DOCKER_HOST: \"unix:\/\/\/var\/run\/docker.sock\"\n\ntest:\n  stage: test\n  script:\n    - npm ci\n    - npm test --if-present\n\nbuild:\n  stage: build\n  script:\n    - docker build -t registry.example.com\/myapp:$CI_COMMIT_SHORT_SHA .\n    - docker tag registry.example.com\/myapp:$CI_COMMIT_SHORT_SHA registry.example.com\/myapp:latest\n    - echo $REGISTRY_PASS | docker login registry.example.com -u $REGISTRY_USER --password-stdin\n    - docker push registry.example.com\/myapp:$CI_COMMIT_SHORT_SHA\n    - docker push registry.example.com\/myapp:latest\n  only:\n    - main\n\ndeploy:\n  stage: deploy\n  script:\n    - docker compose pull\n    - docker compose up -d --remove-orphans\n    - docker image prune -f\n  environment:\n    name: production\n  only:\n    - main<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"option-c-jenkins-pipeline-on-linux\">Option C: Jenkins Pipeline on Linux<\/h3>\n\n\n\n<p>Jenkins is a powerhouse for complex workflows and multi environment promotion.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Install Java and Jenkins\nsudo apt -y install openjdk-17-jre\ncurl -fsSL https:\/\/pkg.jenkins.io\/debian-stable\/jenkins.io-2023.key | sudo tee \\\n  \/usr\/share\/keyrings\/jenkins-keyring.asc &gt; \/dev\/null\necho deb &#91;signed-by=\/usr\/share\/keyrings\/jenkins-keyring.asc] \\\n  https:\/\/pkg.jenkins.io\/debian-stable binary\/ | sudo tee \\\n  \/etc\/apt\/sources.list.d\/jenkins.list &gt; \/dev\/null\nsudo apt update &amp;&amp; sudo apt -y install jenkins\nsudo systemctl enable --now jenkins\n# Access: http:\/\/your_server_ip:8080 (retrieve initial admin password)<\/code><\/pre>\n\n\n\n<p><strong>Example Jenkinsfile (Declarative):<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pipeline {\n  agent any\n  environment {\n    REGISTRY = 'registry.example.com'\n    IMAGE = 'myapp'\n  }\n  stages {\n    stage('Checkout') {\n      steps { checkout scm }\n    }\n    stage('Test') {\n      steps {\n        sh 'npm ci'\n        sh 'npm test --if-present'\n      }\n    }\n    stage('Build &amp; Push Image') {\n      steps {\n        withCredentials(&#91;usernamePassword(credentialsId: 'registry-creds', usernameVariable: 'USER', passwordVariable: 'PASS')]) {\n          sh '''\n            echo \"$PASS\" | docker login $REGISTRY -u \"$USER\" --password-stdin\n            TAG=$(git rev-parse --short HEAD)\n            docker build -t $REGISTRY\/$IMAGE:$TAG .\n            docker tag $REGISTRY\/$IMAGE:$TAG $REGISTRY\/$IMAGE:latest\n            docker push $REGISTRY\/$IMAGE:$TAG\n            docker push $REGISTRY\/$IMAGE:latest\n          '''\n        }\n      }\n    }\n    stage('Deploy') {\n      steps {\n        sh 'docker compose pull &amp;&amp; docker compose up -d --remove-orphans'\n      }\n    }\n  }\n  post {\n    always { sh 'docker image prune -f || true' }\n  }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"option-d-lightweight-bash-ci-cd-no-orchestrator\">Option D: Lightweight Bash CI\/CD (No Orchestrator)<\/h3>\n\n\n\n<p>For very small projects, use a post receive Git hook or a cron triggered script to build and deploy. Keep it simple but secure.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/usr\/bin\/env bash\nset -euo pipefail\n\nREPO_DIR=\/opt\/myapp\ncd \"$REPO_DIR\"\n\necho \"==&gt; Pulling latest...\"\ngit fetch --all\ngit checkout main\ngit pull --ff-only\n\necho \"==&gt; Running tests...\"\nnpm ci\nnpm test --if-present\n\necho \"==&gt; Building &amp; deploying...\"\ndocker build -t myapp:latest .\ndocker compose up -d --build --remove-orphans\ndocker image prune -f<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"secrets-environment-variables-and-rollbacks\">Secrets, Environment Variables, and Rollbacks<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Store secrets in the platform (GitHub Secrets, GitLab CI variables, Jenkins Credentials). Never commit .env files.<\/li>\n\n\n\n<li><strong>Use environment specific configs:<\/strong> .env.staging and .env.production with a secure secrets manager if possible.<\/li>\n\n\n\n<li>Tag releases (e.g., semantic versions) and keep the previous version available for quick rollback with Docker Compose or a symlinked release directory.<\/li>\n\n\n\n<li>Blue\/green or canary deployments reduce risk for high traffic apps.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"security-hardening-checklist\">Security Hardening Checklist<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Principle of least privilege: <\/strong>run runners with non root users; limit sudo.<\/li>\n\n\n\n<li><strong>Network: <\/strong>close unused ports with UFW; isolate build and prod networks if possible.<\/li>\n\n\n\n<li><strong>SSH: <\/strong>key based authentication, disable password login, rotate keys regularly.<\/li>\n\n\n\n<li><strong>Dependencies: <\/strong>scan images with Trivy or Grype; pin base images and patch frequently.<\/li>\n\n\n\n<li><strong>Logs and alerts:<\/strong> enable fail2ban; ship logs to a central system; monitor runner services.<\/li>\n\n\n\n<li><strong>Backups and snapshots: <\/strong>automate via your hosting panel; test restore monthly.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"troubleshooting-and-observability\">Troubleshooting and Observability<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Check services: <\/strong>systemctl status jenkins, gitlab runner, or actions.runner.*<\/li>\n\n\n\n<li><strong>Runner logs: <\/strong>journalctl -u &#8220;actions.runner*&#8221; -e, journalctl -u gitlab-runner -e<\/li>\n\n\n\n<li><strong>Docker logs:<\/strong> docker ps, docker logs &lt;container&gt;, docker events<\/li>\n\n\n\n<li><strong>Nginx: <\/strong>sudo tail -f \/var\/log\/nginx\/access.log \/var\/log\/nginx\/error.log<\/li>\n\n\n\n<li><strong>Common failures: <\/strong>missing environment variables, incorrect registry credentials, firewall blocking outbound registry or package mirrors.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"cost-and-scaling-considerations\">Cost and Scaling Considerations<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Start with 2\u20134 vCPU, 4\u20138 GB RAM; add CPU for builds and RAM for Docker layers.<\/li>\n\n\n\n<li>Use build caches and runners pinned to the server hosting your container registry for speed.<\/li>\n\n\n\n<li>Separate CI from production workloads as you grow to avoid resource contention.<\/li>\n\n\n\n<li>If you need predictable performance, consider multiple runners and a dedicated registry.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"end-to-end-example-from-push-to-production\">End to End Example: From Push to Production<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Developer pushes to main; CI pulls code.<\/li>\n\n\n\n<li>Run unit tests and linting.<\/li>\n\n\n\n<li>Build Docker image, tag with commit SHA and latest.<\/li>\n\n\n\n<li>Push to registry and update docker compose on the server.<\/li>\n\n\n\n<li>Compose recreates the container with zero or minimal downtime.<\/li>\n\n\n\n<li>Health check endpoint confirms success; alerts fire on failures.<\/li>\n<\/ul>\n\n\n\n<p>This flow is resilient, auditable, and easy to replicate for staging and production with different variables and DNS records.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"best-practices-to-keep-your-ci-cd-healthy\">Best Practices to Keep Your CI\/CD Healthy<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Keep pipelines fast:<\/strong> cache dependencies and parallelize tests.<\/li>\n\n\n\n<li><strong>Make failures loud: <\/strong>fail fast, notify Slack\/Email, and include logs.<\/li>\n\n\n\n<li><strong>Immutable artifacts: <\/strong>build once, promote through environments with the same image.<\/li>\n\n\n\n<li><strong>Idempotent deploys: <\/strong>running deploy twice should not break your app.<\/li>\n\n\n\n<li><strong>Document everything: <\/strong>READMEs, runbooks, rollback steps.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"faqs\">FAQs<\/h2>\n\n\n<div id=\"rank-math-faq\" class=\"rank-math-block\">\n<div class=\"rank-math-list \">\n<div id=\"faq-question-1765962557434\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \" class=\"rank-math-question \" id=\"how-do-i-choose-between-jenkins-github-actions-and-gitlab-ci\">How do I choose between Jenkins, GitHub Actions, and GitLab CI?<\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Use GitHub Actions if your code lives on GitHub and you want simple YAML workflows. Choose GitLab CI for integrated GitLab repos and group level runners. Pick Jenkins for highly customized pipelines, complex approvals, or when you need deep plugin support and multi repo orchestration.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1765962584457\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \" class=\"rank-math-question \" id=\"is-docker-required-for-ci-cd-on-linux\">Is Docker required for CI\/CD on Linux?<\/h3>\n<div class=\"rank-math-answer \">\n\n<p>No, but Docker simplifies reproducible builds and deployments. Without Docker, you can run language native builds (Node, Python, Java) and deploy via rsync or systemd services. For consistency and portability, containers are strongly recommended.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1765962599333\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \" class=\"rank-math-question \" id=\"how-do-i-secure-secrets-in-my-pipeline\">How do I secure secrets in my pipeline?<\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Store secrets in your CI platform (GitHub Secrets, GitLab Variables, Jenkins Credentials). Avoid plaintext .env files in repos. For advanced setups, consider HashiCorp Vault or cloud KMS. Limit who can read secrets and rotate them regularly.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1765962611205\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \" class=\"rank-math-question \" id=\"whats-the-easiest-way-to-roll-back-a-bad-deployment\">What\u2019s the easiest way to roll back a bad deployment?<\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Keep the previous Docker image tag and revert docker compose.yaml to that tag, then run docker compose up -d. If you use non containerized deploys, maintain a symlinked \u201ccurrent\u201d release and switch it back, then restart the service.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1765962627682\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \" class=\"rank-math-question \" id=\"can-i-run-ci-cd-on-a-small-vps\">Can I run CI\/CD on a small VPS?<\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Yes. For small projects, a 2 vCPU\/4 GB RAM VPS is sufficient. Use caching and avoid heavy parallel builds. As your pipelines grow, scale up resources or split CI runners from production workloads. Providers like YouStable make vertical and horizontal scaling straightforward.<\/p>\n\n<\/div>\n<\/div>\n<\/div>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>To create CI\/CD on a Linux server, provision a secure host, connect your Git repository, install a CI runner or [&hellip;]<\/p>\n","protected":false},"author":21,"featured_media":18890,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[350],"tags":[],"class_list":["post-14239","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-knowledgebase"],"acf":[],"featured_image_src":"https:\/\/www.youstable.com\/blog\/wp-content\/uploads\/2025\/12\/How-to-Create-CI-CD-on-Linux-Server-with-Docker-and-Git.jpg","author_info":{"display_name":"Sanjeet Chauhan","author_link":"https:\/\/www.youstable.com\/blog\/author\/sanjeet"},"_links":{"self":[{"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/posts\/14239","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/users\/21"}],"replies":[{"embeddable":true,"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/comments?post=14239"}],"version-history":[{"count":8,"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/posts\/14239\/revisions"}],"predecessor-version":[{"id":18893,"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/posts\/14239\/revisions\/18893"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/media\/18890"}],"wp:attachment":[{"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/media?parent=14239"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/categories?post=14239"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.youstable.com\/blog\/wp-json\/wp\/v2\/tags?post=14239"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}