--- title: CI/CD Deployment description: "Automate build, test, and release. It speeds up deploys, but database migrations and rollback need planning." canonical: https://penasihathosting.com/en/hosting-wiki/ci-cd-deployment type: wiki locale: en updated: 2026-10-29 author: Willya Randika --- # CI/CD Deployment ## Overview - **Summary:** Automate build, test, and release. It speeds up deploys, but database migrations and rollback need planning. - **Author:** Willya Randika ([profile](/penulis/willya-randika)) ## Article CI/CD is the practice of automating build, test, and release. Every repository change triggers a pipeline: code is checked out, dependencies installed, tests run, artifacts built, and the result shipped to a server. The goal is fewer manual steps and more consistent releases. ## Pipeline stages A healthy pipeline starts with the correct branch, installs dependencies from the lockfile, runs lint and tests, then builds an artifact or Docker image. After that it deploys to staging and runs smoke tests. If that passes, it promotes to production with approval or a schedule. Finally, a health check and rollback if needed. Fast tests are more useful than a long pipeline that rarely runs. Start with lint, unit tests, and build. Add staging and rollback once the basic flow is stable. ## A GitHub Actions example ```yaml name: deploy on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 cache: npm - run: npm ci - run: npm test -- --run - run: npm run build - name: Deploy env: SSH_KEY: ${{ secrets.SSH_KEY }} SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }} run: | install -m 600 /dev/stdin ~/.ssh/id_ed25519 <<< "$SSH_KEY" printf '%s\n' "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts rsync -az --delete .next/ ${{ secrets.USER }}@${{ secrets.HOST }}:/var/www/app/.next-new/ ssh ${{ secrets.USER }}@${{ secrets.HOST }} 'cd /var/www/app && mv .next .next-old && mv .next-new .next && pm2 restart app' ``` This example is simple. Real production should use immutable artifacts, health checks, and automatic rollback. Never store a private key in the repository. Use a secret manager. Store the host key in `SSH_KNOWN_HOSTS` and verify its fingerprint. Do not rely on `ssh-keyscan` during deployment, because it trusts whatever host key answers. ## Atomic deploys and rollback Instead of overwriting the application folder, deploy to a new directory and switch a symlink with `ln -sfn /var/www/releases/2026-10-30 /var/www/current`. If the health check fails, point the symlink back to the previous release. For Docker, run a versioned image tag and keep the old image until the new release is stable. For databases, prepare backward-compatible migrations and back up before major changes. ## Secrets and access Use encrypted secrets, not variables printed in logs. Restrict SSH keys to the required commands and hosts. Use a dedicated deploy user, not root. Separate staging and production access, and rotate credentials if logs or artifacts leak. ## Common mistakes Automatic deploy without tests only ships bugs faster. A database migration without a rollback plan may be incompatible with the old version. No health check makes a deploy look successful even when the app errors. Shipping `.env` in artifacts also spreads secrets to too many places. Test from staging because production differs. CI/CD relates to [staging](/en/hosting-wiki/staging), [SSH](/en/hosting-wiki/ssh), [Docker](/en/hosting-wiki/docker), and [VPS Snapshot](/en/hosting-wiki/vps-snapshot). ## FAQ No. Small projects also benefit from automated tests and consistent deploys. Start with a simple pipeline, then add staging and rollback. It is safe with tests, health checks, permissions, and rollback. For risky changes, use manual approval or a deploy window. Make backward-compatible migrations, back up first, test in staging, and prepare a rollback plan. Never run a destructive migration at the same time as a code deploy.