← Back to all posts

DevOps Learning Roadmap: Ship and Recover One Small Application

Build a DevOps study sequence around a small HTTP service: verify a release, diagnose a failure, restore content, and decide when to add Git, automation, and containers.

A softly modeled operator fitting a blue application module into a small server

A web page answers on port 8080. You change a file, the response becomes wrong, and restarting the process does nothing to repair it. That small failure is enough to give a DevOps learning roadmap a useful shape: know what you delivered, detect when it is wrong, and restore a known result.

Start with one service you can explain and recover. Add version control, automated checks, and deployment tooling around that service as each solves a problem you have actually encountered. You do not need a Kubernetes cluster to discover that an HTTP 200 response can contain the wrong release.

This is a first-project sequence for someone who can navigate directories and edit a text file. It is not a complete career curriculum. The worked example uses a static page as the application artifact so that programming complexity does not hide the delivery problem.

Define the result before choosing tools

Your first deliverable is a page containing a release marker. Your learning record should establish:

Question Evidence to keep
What should be served? The source file and intended marker
What is actually served? A request to a named URL and its response
Which process serves it? The start command and document directory
Can you detect a bad change? A check that rejects the wrong marker
Can you recover? The restored response, not just a backup file

A running process answers only one of these questions. A successful request answers another. Neither identifies whether the response matches your intended release.

If paths and shell redirection are still unfamiliar, use the Linux learning roadmap first. You need enough shell fluency to distinguish a wrong directory from a broken server.

Serve an explicit directory

Use two terminals on the same machine, with Python 3.7 or later and curl installed. These are development commands; Python’s HTTP server documentation explicitly excludes production use. Keep this exercise bound to loopback and serve only the dedicated directory below.

In terminal one, create a new project directory. If this name already exists, choose another rather than mixing the exercise with existing files:

mkdir devops-service-demo
cd devops-service-demo
mkdir site releases
printf '<h1>release-one</h1>\n' > site/index.html
cp site/index.html releases/release-one.html
python3 -m http.server 8080 --bind 127.0.0.1 --directory site

Leave the server running in the foreground. If port 8080 is occupied, choose an unused port and use it consistently in the following requests. Do not stop an unfamiliar process merely to free the example’s port.

In terminal two:

curl --noproxy '*' -fsS http://127.0.0.1:8080/index.html

The response should contain <h1>release-one</h1>. The address identifies the machine running curl. If your terminals are inside a remote lab, run this request there too; your laptop’s 127.0.0.1 is a different destination.

Here --noproxy '*' keeps the request out of configured proxies. The curl manual documents -f for treating HTTP errors as failure and -sS for suppressing the progress meter while retaining error messages. These flags do not verify the response body.

Make a failure that a status check misses

In terminal two, change into the same devops-service-demo directory using its actual path, then replace the served content:

printf '<h1>wrong-release</h1>\n' > site/index.html
curl --noproxy '*' -sS -o /dev/null -w '%{http_code}\n' \
  http://127.0.0.1:8080/index.html

The server can still return 200. The file exists and the request succeeds, even though the content is wrong.

Add a check for the intended marker:

if curl --noproxy '*' -fsS http://127.0.0.1:8080/index.html \
  -o response.html && grep -q '<h1>release-one</h1>' response.html; then
  printf 'release check passed\n'
else
  printf 'release check failed\n'
fi

The check should fail after the edit. Using a separate downloaded file also lets you inspect what the server actually returned. The && prevents the content check from accepting an old file when curl itself fails.

This is a deliberately narrow check. A marker does not prove that every link or application feature works. It does establish a requirement that a bare status code misses. Later, choose checks that represent the behavior your application actually promises.

Restore content, then test a different failure

Restore the saved artifact:

cp releases/release-one.html site/index.html
curl --noproxy '*' -fsS http://127.0.0.1:8080/index.html

Run the release check again. It should now pass without restarting Python: the service reads the file for a new request. Restarting a process and restoring content are different operations.

Next, press Ctrl+C in terminal one to stop the server. A new curl request should fail to connect if no other process has taken that port. Restore service by rerunning the Python command from the project directory, then repeat both the request and content check.

You have now observed two failures with different repairs:

  • Wrong content: restore the intended artifact.
  • Stopped process: start the process with the intended configuration.

Keep those distinctions in your notes. A habit of restarting everything can conceal the original fault without establishing that the correct release is available.

The releases copy demonstrates restoration within one machine. It is not an off-machine backup, and it would not survive losing that machine’s storage. A real application’s recovery plan must also account for configuration and changing data; restoring an old executable does not necessarily reverse a database migration.

Turn the exercise into a study sequence

Once you can reproduce those checks, each next topic has a reason to exist.

Version control answers which change you intended. Put the source and your check into a small Git repository when practicing independently. Learn to inspect a diff and record a coherent change before studying branching strategies. The Git book’s guide to recording changes explains the working tree, staging area, and commits. A commit identifies source history; it does not by itself prove what a server currently runs. Do not store secrets or generated logs with source merely because they share a directory.

Shell automation answers whether you can repeat the check. Move the known commands into a script with meaningful failure exits. Run it against the correct page, the wrong page, and a stopped server. A script that prints “failed” but exits successfully can mislead the next automation layer.

Service management answers who starts and supervises the process. The foreground server ends with your terminal interaction. On a Linux system using systemd, study service status, startup configuration, and logs through Service Management with systemd. Do not assume that putting & after a command supplies the same lifecycle behavior.

Containers answer a packaging problem. Once the source, runtime, and response checks are clear, package an application and compare its build inputs with its running instance. The image versus container walkthrough demonstrates why rebuilding an image does not update an existing container.

A delivery pipeline answers who repeats the steps. Only then move build and verification commands into CI. Write down which artifact was checked and which artifact was deployed. Keep credentials outside the repository, and retain a way to return to the previous known artifact.

You can use the LabEx DevOps path to find practice for these gaps. Its current course catalog includes Linux foundations, Git, service management, and later delivery tools. The order above is an editorial project plan, not a claim that every course uses this exact application.

Finish with a handoff someone else can follow

Write a short runbook containing the prerequisites, start command, expected URL and marker, failure check, restoration command, and cleanup. Give paths relative to a named project directory so another reader does not have to guess where to run them.

When done, stop the foreground server with Ctrl+C. Keep the tiny project as a learning artifact, or remove its known files and empty directories:

rm site/index.html releases/release-one.html response.html
rmdir site releases

Run these from the project directory; response.html exists if you ran the release check. Review any extra files before deleting them.

Your next milestone is to recreate the service from those notes in a fresh workspace, introduce the wrong release, and recover it. If the notes omit a dependency or a working directory, that is the next thing to fix before adding another tool.

References