Skip to content

Blog

Who owns the deploy? Dividing Git, CI, servers, and release state

Separate repository, CI runner, server, and deployment-state ownership—and see where Appaloft fits without pretending to manage every layer.

AppaloftDeployment controlDocker ComposeSelf-hostingCI/CD

When a small team puts an application on a server, the first discussion is usually about tools: Railway, Render, Dokku, Portainer, Docker Compose, or a shell script over SSH.

The tool matters, but it can hide a more useful question. Who owns the code, where does CI execute, who owns the server, and where does deployment state live afterward? One platform can own all four. They can also be split. When the boundary is implicit, adding tools makes it harder to answer what is actually running.

Appaloft draws a narrower boundary. Code stays in Git. Builds can stay in the CI system a team already uses. Applications run on servers the team controls. Appaloft connects the source, target server, deployment record, health evidence, access state, and application recovery path. It is not a Git host, a CI runner manager, or a general-purpose server control panel.

Four things can have four owners

A typical small-team deployment path contains at least four separately owned objects.

Source ownership belongs to the Git repository. The repository holds source code, Dockerfiles, Compose manifests, and non-secret deployment configuration. Whether it lives on GitHub, GitLab, Gitea, or another Git service, it should not quietly become the database for production runtime state.

Execution ownership belongs to a CI runner or the person initiating a deployment. A runner can check out code, test it, build it, and call a deployment entrypoint. It needs short-lived authority to execute work. That does not make it the long-term owner of the server, deployment records, or recovery policy.

Infrastructure ownership belongs to whoever controls the server or cloud account. VPS instances, SSH credentials, DNS providers, and registries have their own permission and billing boundaries. Giving a client the server account does not make an undocumented deployment script maintainable.

Deployment-state ownership belongs to the control plane. It records which source entered the deployment, which runtime plan was used, which server was targeted, whether health checks passed, where access points, and whether a previous application deployment is available as a recovery candidate.

A managed PaaS may own all four. With servers you control, they are usually split. The design problem is how to reconnect them without erasing their boundaries.

CI should trigger a deploy, not become the deployment database

GitHub Actions, Gitea Actions, and self-managed runners are good at executing work: check out the repository, run tests, build an image, and invoke a CLI. Trouble starts when every deployment fact remains in the CI log.

Weeks later, a green job proves that a script returned a zero exit code at that time. By itself, it does not prove that the server pulled the intended artifact, the route changed, the health endpoint passed, or the running workload still belongs to that deployment.

We prefer to use CI as an entrypoint and keep durable state in the deployment control plane. The official GitHub Action is one integration Appaloft currently provides. Other CI systems can invoke the same CLI or API, but Appaloft does not create, isolate, or maintain those runners.

This is also why “accepts a Git source” is different from “natively manages every Git provider.” The repository supplies source. The runner supplies an execution environment. The deployment control plane should not pretend to own either one.

Compose is deployment input, not complete server management

Docker Compose works well for small applications. It keeps service, network, volume, and environment relationships in a reviewable file. It is easier to recover than a series of docker run commands left in shell history.

Appaloft can accept a Compose manifest as a deployment source and model a multi-service stack as one deployable Resource. For example:

name: acme-web
source:
  path: .
runtime:
  strategy: docker-compose
  dockerComposeFilePath: docker-compose.yml
network:
  internalPort: 8080
health:
  path: /health

This configuration describes the source, runtime shape, public entrypoint, and health check. It does not hand the entire server to Appaloft. Operating-system updates, disk capacity, the Docker daemon, backup media, and host security remain server-operations work.

Appaloft is not a drop-in Portainer replacement either. It does not adopt every arbitrary existing container as a controlled deployment, and it does not claim general image-vulnerability scanning, host filesystem browsing, or universal container administration. Its scope is the deployments Appaloft creates and records, and the evidence needed to verify and recover them.

A running process is weak deployment evidence

A running process does not prove that a new version took traffic. An endpoint returning 200 may only mean that the old container remains healthy.

A useful verification path checks several facts separately:

  • the runtime started the workload associated with this deployment;
  • the health-profile path returned the expected status;
  • the route still points to the current Resource;
  • deployment record, runtime identity, and access result agree;
  • a previous verified deployment remains available as a recovery candidate.

An Appaloft health profile keeps path, expected status, timeout, retry, and start-period policy on the Resource so later deployments can reuse it. Editing that profile does not rewrite historical deployment snapshots. Logs and health observations also return to the same Resource instead of remaining scattered across CI, SSH sessions, and proxy dashboards.

The goal is not a larger dashboard. It is fewer contradictory answers. If CI reports success, the container is healthy, and the domain is unreachable, a control plane needs to show those as separate facts instead of choosing one green signal as the verdict.

Application rollback does not restore every kind of state

Appaloft rollback creates a new application deployment attempt from a retained successful deployment snapshot and Docker/OCI artifact identity. It does not re-plan an old version from the current Resource profile, and it does not overwrite the failed deployment record.

The limitation matters: it does not automatically roll back databases, volumes, or external dependencies.

If a release ran an irreversible database migration, switching to the old image can make the incident worse. A named volume in Compose remains runtime state; it does not return to yesterday when the application image changes. Object storage, queues, and third-party APIs have their own recovery procedures.

A practical recovery plan therefore has at least two layers:

  1. Application layer: retain verified deployments, artifact identity, and configuration snapshots so an application rollback can be evaluated.
  2. Data layer: maintain backups, restore rehearsals, migration-compatibility rules, and explicit human confirmation boundaries.

When the second layer is missing, the product should show a manual recovery gap. A button labelled “Rollback” is not a complete disaster-recovery system.

Managed PaaS or server ownership?

If the goal is to publish a first Spring Boot, Node, or static application quickly, a managed PaaS may be the better choice. Database, TLS, logs, and deployment entrypoints are already collected in one service. The team does not need to learn server maintenance first.

The own-server path fits a different set of constraints: the team already has a VPS or private machine, wants to retain runtime and configuration control, needs CLI, CI, Web, and agents to share deployment state, or no longer wants production to depend on an SSH script only its author understands.

This is not a contest between “easy” and “professional.” It is a choice about who pays the maintenance cost. A managed platform absorbs part of that responsibility. Server ownership keeps it with the team, while a control plane reduces repeated work and fragmented state.

Appaloft serves the latter path. Start with deployment sources to identify whether the current artifact is a local folder, Git repository, Docker image, or Compose manifest. Configure a health and network profile so verification checks the real access path. When a deployment fails, read deployment recovery state before choosing retry, redeploy, or application rollback.

One product does not need to own the whole deployment chain. Every object does need an explicit owner, and every handoff needs readable state. Once a team can answer where the code lives, who executes changes, who controls the server, and who preserves deployment facts, choosing tools becomes much easier.