Deployment console
The deployment console is a web page that shows one button per project and runs, on the server, the delivery script that this project already has, so that a release requires neither an account on the server nor tooling on the machine of whoever triggers it.
- Role
- Sole author
- Period
- August 2026
- Status
- In service. In service; twenty actions, a single account declared to date
- Technologies
- OliveTin, Docker Compose, Apache, shell scripts
- Repositories
- One
Context
Each of my projects is delivered by a script placed in its repository, which updates the code on the server then rebuilds its containers. This works as long as I am the only one delivering, since it requires an account on the server, the keys that allow the repositories to be read from there and, for some projects, the token that announces the delivery.
Problem
Entrusting a delivery to a collaborator amounted to opening the whole server to them, when all they need is to trigger a known operation. Writing a separate deployment tool would moreover have created a second way of delivering, with nothing to guarantee that it would stay identical to the first.
Answer
Calling the existing script
The console is built on OliveTin, whose image already ships docker, git and ssh. Each button comes down to one line, which moves into the project’s directory and runs its script there. Nothing is rewritten: the repository’s script remains the single source of truth, and a delivery launched from the console cannot differ from a delivery launched from a terminal. The environment, production or staging, is an argument of the button, limited to a closed list.
- title: Sanu - API Django
shell: cd "$PROJECTS_ROOT/sanu-api-django" && bash ci/{{ environnement }}.sh
timeout: 1800
maxConcurrent: 1
acls: [devs]
arguments:
- name: environnement
title: Environnement
type: ascii_identifier
default: production
choices:
- value: production
title: Production
- value: staging
title: RecetteDenying by default
All permissions are denied by default. The administrators group sees every action, while the developers group sees only those that name it in their access list. Adding an action therefore makes it visible to no one until a list allows it, and an oversight results in a missing button rather than in one access too many. Each delivery action is moreover limited to one execution at a time and bounded by a timeout.
The same path on both sides
The scripts call Docker from the console’s container, but it is the host’s daemon that resolves the build contexts. The projects directory is therefore mounted in the container at the exact path it occupies on the host, otherwise a build would look for files where they are not. Finally, the version of the image is pinned, so that a restart of the server does not change the console without anyone having decided it, and the redeployment of the console waits for its health check to respond before declaring itself successful.
Limits and outlook
A console able to drive Docker and to read the repositories amounts to full access to the machine, and it seemed preferable to me to write it in its documentation rather than to leave it to be guessed. Three precautions follow from it, which are not optional: the console listens only locally, behind an encrypted connection, guest mode is closed, and the image is kept up to date. A second barrier in front of the console, by filtering or by an authentication service, remains to be put in place.
Delegation, which justified the project, has not yet been exercised: a single account is declared, and the developers group has no member. The console moreover keeps no trace of a delivery other than its logs. Finally, the most recent Sanu repositories are delivered by their pipelines, so that part of the console’s scope has shifted since it was written and some of its actions need to be reviewed.
