Continuous Integration and Delivery on GitHub Actions
Deploying a microservice without CI/CD is four hours of routine: build locally, scp to server, systemctl restart, prayers. GitHub Actions is the de facto standard for projects on GitHub, but "it works" and "it works correctly in production" are different things. We have been doing DevOps outsourcing for over 10 years and have verified on a hundred projects: most self-written pipelines on Actions have a typical set of problems. Secrets are stored not in GitHub Secrets but in env variables right in YAML. No environment matrix, actions/checkout without fetch-depth: 0 breaks semantic versioning, and deployment goes without health-check afterwards.
Let's take a real case: a fintech startup spent up to 4 hours on manual microservice deployment. After implementing a pipeline on GitHub Actions with automated tests, build, and Helm deployment, the time dropped to ~30 minutes. Additionally, we set up approval for production via Environments — no deployment passes without code review and confirmation. Time savings on deployment — 87%, reduction in error count — 80%.
Why automate deployment via GitHub Actions?
Manual deployment is the main source of errors: forgot a secret, pushed the wrong branch, deployed the wrong image. GitHub Actions provides a single interface for building, testing, and delivery. Unlike Jenkins, no separate server to set up — runners are already there. And compared to GitLab CI, Actions is more tightly integrated with the GitHub ecosystem: issues, pull requests, code review. According to our measurements, automation reduces feature rollout time from 2 hours to 15 minutes. GitHub Actions runs the pipeline 2 times faster than Jenkins for a typical task.
Pipeline architecture for a production-ready project
The minimal working structure for a service deployed to Kubernetes looks like this: three workflow files with clear responsibility zones.
-
ci.yml— triggered on every push and pull_request. Linting, unit tests, Docker image build, Trivy scanning. No deployments. -
deploy-staging.yml— triggered on merge tomain. Deploy to staging via Helm, smoke tests, Slack notification. -
deploy-production.yml— triggered manually viaworkflow_dispatchor by tagv*.*.*. Deploy to production with approval through GitHub Environments.
# .github/workflows/deploy-production.yml
name: Deploy to Production
on:
push:
tags:
- 'v*.*.*'
jobs:
deploy:
runs-on: ubuntu-22.04
environment:
name: production
url: https://app.example.com
steps:
- uses: actions/checkout@v4
- name: Configure kubectl
uses: azure/k8s-set-context@v3
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBECONFIG_PROD }}
- name: Deploy via Helm
run: |
helm upgrade --install myapp ./charts/myapp \
--namespace production \
--set image.tag=${{ github.ref_name }} \
--set replicaCount=3 \
--wait --timeout=5m
- name: Verify deployment
run: |
kubectl rollout status deployment/myapp -n production --timeout=3m
Key point: environment: production activates required reviewers — deployment won't proceed without approval from the designated person. This is a built-in mechanism, no external approval systems needed.
How to protect secrets in Actions?
GitHub Secrets is the right place for everything sensitive. But there's a nuance: Organization-level secrets are accessible to all organization repositories, which creates a risk. For production credentials we use Environment secrets, which are available only in a specific environment and only when the protection conditions are met.
For integration with HashiCorp Vault:
---
- name: Get secrets from Vault
uses: hashicorp/vault-action@v2
with:
url: ${{ secrets.VAULT_ADDR }}
token: ${{ secrets.VAULT_TOKEN }}
secrets: |
secret/data/myapp/prod DB_PASSWORD | DB_PASSWORD ;
secret/data/myapp/prod API_KEY | EXTERNAL_API_KEY
--- Caching and build time optimization
Without caching of Node.js or Python dependencies, the pipeline spends 3–5 minutes just on npm install. Standard solution:
---
- uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
For Docker layers we use cache-from and cache-to via GitHub Container Registry or BuildKit inline cache. This speeds up the build 3 times on a typical project.
Self-hosted runners for private infrastructure
GitHub-hosted runners do not have access to private networks. If the Kubernetes cluster is behind a VPN or in a private subnet, self-hosted runners are needed. We run them as a Deployment in the same cluster via actions-runner-controller — ARC automatically scales the number of runner pods under load.
How to set up approval for production deployment?
GitHub Environments allow configuring required reviewers — a group of people without whose approval the deployment to production will not start. This is a built-in mechanism, no external approvals needed. At the environment level, you can set branch protection and time zone restrictions.
What's included in the work?
| Deliverable | Description |
|---|---|
| Audit of current workflows | check the structure, security, and performance of existing pipelines |
| Architecture design | CI, staging, production workflows, environment matrix, protection rules |
| CI and CD implementation | writing YAML files, configuring secrets, caching, approval |
| Monitoring integration | notifications in Slack/Telegram, Grafana dashboards for deployments, alerting on failures |
| Documentation | runbook for developers, deployment process description, troubleshooting |
| Team training | workshop on pipeline usage, code review of workflows |
| Support | warranty maintenance 1 month after delivery, then according to SLA |
Project stages and timelines
- Audit — analysis of current infrastructure, repositories and processes. Issue a report with recommendations.
- Design — pipeline architecture, tool selection (Helm, Kustomize, ArgoCD), agreement with team.
- Implementation — writing CI and CD workflows, setting up Environments, Vault integration, caching.
- Testing — run all pipelines on staging, check fallback scenarios, rollback.
- Deployment — roll out to production, monitor first deployments, make adjustments as needed.
| Project Complexity | Timeline |
|---|---|
| One service, basic deployment | 1–2 days |
| Monorepo or multiple services | 2–3 days |
| Self-hosted runners + K8s + Vault | 3–5 days |
The cost is calculated individually after an infrastructure audit. Contact us for a consultation — we will evaluate your project and offer the best solution.
Example of successful implementation: a fintech project with 5 microservices, 3 environments, self-hosted runners. Deployment time reduced from 4 hours to 25 minutes, release frequency increased 6 times. Full payback in 3 months.
Our guarantees: all configurations are covered by tests, deployed using Infrastructure as Code (Terraform/Ansible), and full documentation is provided after delivery. Team experience — over 10 years in DevOps, 50+ successful CI/CD implementations on GitHub Actions. Order an audit of your workflows — get a professional assessment.







