WHAT WE NEED → WHAT GETS IN THE WAY → WHAT HELPS

We need to build an app and deliver changes.

At first, one person can write the app and copy it to a server. The goal is simple: users can use it.

Then more people edit the code. More changes must reach users. New problems appear.
We need to work on the same app.
BUT…

Shared folders do not show changes clearly. We can overwrite work or release code that no one reviewed.

SO…

We use Git + GitLab. Git keeps change history. GitLab gives the team a shared place to review and merge changes.

We need the tested app to run on the server.
BUT…

It works on a laptop, but the server has different libraries. We spend time rebuilding its runtime environment.

SO…

We use Docker to package the app and its dependencies into an image. This reduces differences between environments.

We need to check each change before users receive it.
BUT…

Manual tests can be missed. A written release checklist cannot execute its own commands.

SO…

We define CI/CD jobs in GitLab. A Runner executes the tests, builds and deploy commands. GitLab records the results.

We need to release the exact version that passed tests.
BUT…

Rebuilding later can produce a different image. An image on one laptop is not available to every server.

SO…

We use an image registry to store the built image. Deployment selects its exact digest: the image ID.

The result: the team can review a change, test it, and deliver the same built release. Each tool answers a problem; these are not installation steps.
Docker does not remove all differences: configuration, data, host OS and CPU still matter. A registry and runners can be provided by GitLab. Alternative tools can meet the same needs.
WHAT WE NEED → WHAT GETS IN THE WAY → WHAT HELPS

We need users to keep using the app.

A release is not enough. We also need a place to run it, a way to reach it, and a way to recover it.

The app now has repeated releases, real users and possibly several servers.
We need test and production environments we can rebuild.
BUT…

Manual setup creates differences. After a server fails, we may not know which settings made the app work.

SO…

We use infrastructure as code (IaC). Files describe resources and setup. We review and apply changes with tools such as Terraform and Ansible.

We need app instances to run across available servers.
BUT…

Docker runs containers on a host. By itself, it does not choose hosts across a cluster or manage a cluster-wide rollout.

SO…

We use Nomad to place instances and apply update and recovery rules. It can restart or replace failed tasks, subject to policy and capacity.

We need users to reach the app safely and keep their data.
BUT…

App addresses can move. Open access can expose data. Replacing a container can remove files stored inside it.

SO…

We add DNS, TLS and routing for access; identity and secrets for protection; persistent storage and backups when the app keeps data.

We need to detect problems, recover, and end service safely.
BUT…

A running process can still give errors. A restart cannot restore lost data. Old apps can leave access and costs behind.

SO…

We use monitoring and alerts to detect failures, restore procedures to recover data, and a retirement plan to stop traffic and remove access safely.

The result: users can reach the app, the team can operate it, and changes can continue. Feedback reveals the next need. The cycle repeats.
Choose the capability first, then the product. A small app may not need Nomad or a separate tool for each need. Configure health checks, recovery, capacity and compatible data changes.
Sources: Docker · Runner · IaC · Nomad. Plain English inspired by ASD-STE100; not certified compliance.