Independent marketplace for cloud provider accounts across 22 providers

DevOps & Automation

CI/CD Pipelines That Deploy to Cloud Servers

A green build is not a deployment. This guide covers the machinery between the two: promoting one immutable artifact, giving the pipeline scoped credentials, switching versions without dropping requests, and choosing between push and pull delivery.

A continuous delivery pipeline moving a build artifact from a repository onto cloud servers

The Gap Between a Green Build and Running Code

Continuous integration is well understood. Push a branch, tests run, a badge turns green. Continuous delivery to a server you own is where teams improvise, and improvised deployment is how you end up with one engineer who is the only person allowed to release.

A deployment pipeline to cloud servers has to answer four questions. What exactly are we shipping? How does the pipeline prove it may touch the server? How does the new version replace the old one without dropping requests? And how do we get the previous version back in under a minute?

Everything below is about answering those four questions with boring, repeatable machinery.

Build Once, Then Promote

The single most valuable rule is that the thing you tested is the thing you ship. Build an artifact once, give it an immutable identifier, and move that identifier through environments.

The artifact can be a container image, a tarball, a system package or a compiled binary. What matters is that staging and production install byte identical content. Rebuilding per environment reintroduces the exact class of bug the pipeline exists to prevent, because two builds of the same commit can resolve dependencies differently.

For container based deployments, tag with the commit SHA rather than a moving label, and push to a registry the servers can reach. Where those registries live and how to authenticate to them is covered in container registries and where your images live.

How the Pipeline Earns Access

There are two honest patterns, and one habit worth breaking.

Push based access

The runner connects outward to the server, usually over SSH, and executes the release steps. Generate a dedicated key pair for the pipeline, install the public half for a restricted deploy user, and store the private half as an encrypted secret in the CI system. Never reuse a personal key.

Restrict what that key can do. An entry in the server's authorized keys file can pin a single command, so a stolen key runs the release script and nothing else. Combine it with a firewall rule limiting SSH to the runner's address range where the CI provider publishes one.

Pull based access

The server, or an agent on it, watches a registry or repository and applies changes itself. Nothing inbound is required, which suits fleets and private networks. The trade off is a short reconciliation delay and a second system to operate.

The habit to break

Long lived cloud access keys pasted into CI settings are the most common credential leak in the industry. Most providers now support federated identity, where the CI system presents a signed token and receives temporary credentials scoped to one repository. GitHub documents the mechanism in its deployment hardening guide, and the broader storage question is discussed in managing API keys and secrets in cloud projects.

A Workflow File You Can Read in One Screen

GitHub Actions defines pipelines as YAML in the repository. A deployment job typically declares its trigger, an environment for approval gates, and a small number of steps.

on: push: branches: [main] restricts deployment to the protected branch, while pull requests run only the test job.

The steps themselves are unremarkable. Check out the code, set up the language runtime, run tests, build and push the image, then connect to the target and roll it forward. The connection step is often a single command.

ssh deploy@api.example.com /usr/local/bin/release.sh ghcr.io/example/api:$GITHUB_SHA

Keeping the release logic in a script on the server, rather than in the YAML, has two benefits. You can run the same script by hand during an incident, and switching CI vendors becomes a rewrite of twenty lines instead of two hundred.

GitLab CI, Woodpecker, Drone and Jenkins all express the same shape with different keywords. The structure survives the move.

Replacing the Old Version Without Dropping Requests

On a single virtual machine running a service under systemd, the classic pattern is a releases directory plus a symbolic link. Unpack the new build into a timestamped folder, repoint the current symlink, then restart the unit. Rollback is repointing the link at the previous folder.

For zero dropped connections, the service needs to handle its termination signal gracefully: stop accepting new connections, finish in flight requests, then exit. Pair that with a reverse proxy in front, so the proxy retries briefly while the backend restarts.

Always gate the pipeline on a health check after the restart. Poll a lightweight endpoint until it reports ready, with a timeout, and fail the job loudly if it never does. A deployment that reports success while the process is crash looping is worse than no pipeline at all.

When the service runs across several machines, deploying to all of them at once is the mistake. Roll through them in batches, or run two parallel environments as described in blue-green deployments on cloud infrastructure.

Choosing Between the Two Shapes

QuestionPush from CIPull by agent
Inbound access to serversRequiredNot required
Time to first working setupShortLonger
Behaviour at fleet scaleDegrades as hosts multiplyScales naturally
Where credentials sitIn the CI systemOn the target
Recovery of drifted hostsManualAutomatic on next loop
Best fitA handful of serversClusters and private networks

Most teams start with push because it is three lines of configuration, and move to pull when the host count stops fitting in a list. Neither choice is permanent.

The Pipeline Is Not the Whole Story

A deployment pipeline assumes the target already exists and is configured. Creating the instance, its firewall and its DNS record belongs to a separate workflow, usually driven by infrastructure as code with Terraform.

Keeping the two apart matters. Application releases happen many times a day and should be fast and reversible. Infrastructure changes happen rarely and deserve a slower, more deliberate review. Merging them into one job means every code change asks for permission to modify your network.

If you are building this for the first time, a single inexpensive virtual machine is enough to practise the full loop. The Hetzner account listings explain what such an account gives you, and the AWS versus Hetzner comparison covers what changes when the same pipeline targets a hyperscaler instead.

Common Questions

Should the pipeline run database migrations?

Yes, but as a distinct step that runs before the new code becomes active, and written so that the previous version still works against the migrated schema. Additive migrations first, destructive cleanup in a later release. That discipline is what makes rollback possible at all.

How do I stop secrets appearing in build logs?

Inject them as masked environment variables rather than command arguments, since arguments are visible in process listings and often echoed by the runner. Avoid printing environment dumps in debug steps, and rotate anything that does leak instead of hoping nobody read it.

Is a self hosted runner worth it?

It helps when builds need access to a private network or a large dependency cache. It also means you now operate a machine that executes code from your repository, so isolate it, keep it patched, and be cautious about running it on pull requests from forks.

How fast should a deploy be?

Fast enough that nobody batches changes to avoid it. Long pipelines encourage large, risky releases. Caching dependencies, running test suites in parallel and building images with ordered layers usually recover most of the time without changing the design.

Questions about a cloud account?

Ask about products, variants, availability or purchasing on either channel.