E-NO Logo
EN FR
Linux architecture 9 Min Read

Linux architecture explained with practical examples: practical implementation guide

calendar_today Published: 2026-07-13
update Last Updated: 2026-07-13
analytics SEO Efficiency: 97%
Technical guide illustration for Linux architecture explained with practical examples: practical implementation guide.

Intro

This guide explains Linux architecture in practical terms and shows how to build a small HTTP service you can run locally and extend into containers and orchestration. You will learn how the kernel, processes, filesystems, networking, and init systems work together, how requests flow through the stack, common failure points, and operational tradeoffs. By the end, you will have a working example supervised by systemd, fronted by Nginx, containerized with Docker, and optionally orchestrated with Kubernetes.

Linux architecture at a glance

From bottom to top:

  • Hardware executes instructions and handles device interrupts.
  • The Linux kernel manages CPU scheduling, memory, devices, filesystems, and networking; it exposes a system call interface.
  • The C library (e.g., glibc) and language runtimes wrap syscalls.
  • The init system (often systemd) brings the machine to a usable state and supervises services.
  • User space contains shells (Bash), daemons, and applications.
  • Filesystem namespaces present resources as files under /, with /proc and /sys exposing kernel state.
  • Networking provides sockets via the kernel, with interfaces, routes, firewall rules, and name resolution.

These layers form a consistent model: processes in user space interact with the kernel through file descriptors and syscalls, while the init system controls service lifecycles.

Control flow and data flow

Control flow:

  • A process calls into the kernel via syscalls (open, read, write, socket, connect, accept, fork, execve).
  • systemd uses cgroups to place services into resource-controlled groups and restarts them on failure.
  • Signals (SIGTERM, SIGKILL) control process lifecycles. Timers, sockets, and units in systemd help structure service startup.

Data flow:

  • User space reads and writes file descriptors (files, pipes, sockets). Storage I/O passes through the VFS to filesystem drivers and block layers. Network I/O goes through the socket API to protocol stacks (TCP, UDP) and NIC drivers.
  • A typical web request flows: client -> NIC -> kernel TCP/IP -> server process accept() -> user handler -> filesystem or upstream -> response write() -> kernel TCP/IP -> NIC -> client.

Inspection tips:

uname -a                     # kernel and platform
ps -ef | grep APP            # process tree
ls -l /proc/$/fd            # descriptors of current shell
ss -ltnp                     # sockets and listeners
sudo journalctl -xe          # recent system messages

Practical example: a small HTTP service

We will implement a minimal HTTP JSON service bound to 127.0.0.1:5000, manage it with systemd, place Nginx in front, then containerize it.

Create /opt/app/app.py:

#!/usr/bin/env python3
from http.server import BaseHTTPRequestHandler, HTTPServer
import json

class H(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/healthz":
            self.send_response(200)
            self.send_header("Content-Type", "text/plain")
            self.end_headers()
            self.wfile.write(b"ok\n")
            return
        self.send_response(200)
        self.send_header("Content-Type", "application/json")
        self.end_headers()
        body = {"message": "hello", "path": self.path}
        self.wfile.write(json.dumps(body).encode("utf-8"))

if __name__ == "__main__":
    HTTPServer(("127.0.0.1", 5000), H).serve_forever()

Install and smoke test:

sudo mkdir -p /opt/app && sudo chown -R "$USER":"$USER" /opt/app
chmod +x /opt/app/app.py
/opt/app/app.py &          # quick smoke test
curl -s http://127.0.0.1:5000/ | jq . || curl -s http://127.0.0.1:5000/
kill %1

This sets up a simple request path you can observe end to end.

Managing with systemd and Bash

Use systemd to supervise the service and Bash to manage tasks.

Create /etc/systemd/system/hello.service:

[Unit]
Description=Hello HTTP service
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/opt/app
ExecStart=/usr/bin/env python3 /opt/app/app.py
Restart=on-failure
RestartSec=2s
AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable --now hello.service
sudo systemctl status hello.service --no-pager
curl -s http://127.0.0.1:5000/ | head -n1

Bash helper for common ops, /usr/local/bin/helloctl:

#!/usr/bin/env bash
set -euo pipefail
case "${1:-}" in
  start) sudo systemctl start hello.service ;;
  stop) sudo systemctl stop hello.service ;;
  status) systemctl --no-pager status hello.service ;;
  logs) sudo journalctl -u hello.service -n 200 -f ;;
  reload) sudo systemctl restart hello.service ;;
  *) echo "usage: $0 {start|stop|status|logs|reload}"; exit 2 ;;
esac

Install helper and inspect flows:

sudo install -m 0755 /usr/local/bin/helloctl /usr/local/bin/helloctl
ss -ltnp | grep 5000
ps -o pid, ppid, cmd, pmem, etime -C python3
sudo journalctl -u hello.service -n 50 --no-pager

Control and data flow checkpoints:

  • Control: systemd -> ExecStart -> process -> signals on restart/stop.
  • Data: client -> 127.0.0.1:5000 -> app -> response.

Fronting with Nginx

Put Nginx in front for TLS termination and routing. Install Nginx using your distro package manager, then create /etc/nginx/conf.d/hello.conf:

server {
    listen 80;
    server_name _;

    location /healthz {
        proxy_pass http://127.0.0.1:5000/healthz;
    }

    location /api/ {
        proxy_pass http://127.0.0.1:5000/;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    location / {
        root /usr/share/nginx/html;
        index index.html;
    }
}

Test and reload:

sudo nginx -t && sudo systemctl reload nginx
curl -s http://127.0.0.1/api/ | head -n1

Data flow now: client -> Nginx (port 80) -> loopback -> app (5000) -> Nginx -> client.

Containerizing with Docker

Package the app for portability.

Create /opt/app/requirements.txt (empty for stdlib) and Dockerfile:

FROM python:3.11-slim
WORKDIR /opt/app
COPY app.py .
EXPOSE 5000
USER 1000:1000
CMD ["python", "/opt/app/app.py"]

Build and run:

cd /opt/app
sudo docker build -t hello-app: local .
sudo docker run --rm -p 5000:5000 --name hello hello-app: local &
curl -s http://127.0.0.1:5000/ | head -n1
sudo docker stop hello

You can also manage the container with systemd to unify operations:

[Service]
ExecStart=/usr/bin/docker run --rm --name hello -p 5000:5000 hello-app: local
ExecStop=/usr/bin/docker stop hello
Restart=on-failure

Orchestrating with Kubernetes (optional)

If you use a local cluster (e.g., kind or minikube), deploy with:

hello-deploy.yaml:

apiVersion: apps/v1
kind: Deployment
metadata: { name: hello }
spec:
  replicas: 2
  selector: { matchLabels: { app: hello } }
  template:
    metadata: { labels: { app: hello } }
    spec:
      containers:
      - name: hello
        image: hello-app: local
        imagePullPolicy: IfNotPresent
        ports: [{ containerPort: 5000 }]

hello-svc.yaml:

apiVersion: v1
kind: Service
metadata: { name: hello }
spec:
  selector: { app: hello }
  ports:
  - name: http
    port: 80
    targetPort: 5000
    protocol: TCP
  type: ClusterIP

Apply and test with a port-forward:

kubectl apply -f hello-deploy.yaml -f hello-svc.yaml
kubectl port-forward svc/hello 8080:80 &
curl -s http://127.0.0.1:8080/ | head -n1

Observability, failure points, tradeoffs

Key observability commands:

  • System state: uname -a, uptime, dmesg --level err, warn
  • Processes: ps -ef, pstree -a, systemctl status, journalctl -u NAME
  • Networking: ip addr, ip route, ss -ltnp, dig, curl -v
  • Files and descriptors: lsof -p PID, ls -l /proc/PID/fd

Common failure points:

  • Ports and binding: wrong address (0.0.0.0 vs 127.0.0.1), port in use, firewall rules.
  • Permissions: binding to privileged ports, file perms, SELinux/AppArmor denials.
  • Resource limits: memory pressure causing OOM kills, ulimit nofile too small, CPU throttling.
  • Crashes and restarts: missing dependencies, environment differences, bad signals handling.
  • Proxy issues: Nginx upstream mismatch, timeouts, header size limits, body size limits.
  • Containers: missing image, wrong tag, port mappings, overlay filesystem quirks, DNS in container.
  • Kubernetes: readiness probes failing, Service selectors not matching, insufficient resources.

Tradeoffs:

  • Direct service vs behind Nginx: simplicity vs centralized TLS, routing, caching.
  • Bare process vs container: host integration and speed vs packaging and isolation.
  • systemd vs container runtime supervision: native OS tooling vs image-centric workflows.
  • Orchestration: powerful scaling and rollout control vs added complexity.

Workflow Overview

Follow these steps to build confidence and clarity:

  1. Verify the platform
  • Check kernel and distro: uname -a; cat /etc/os-release
  • Confirm ports are free: ss -ltn | grep -E ":(80|5000)" || true
  1. Build the app
  • Create /opt/app/app.py and run it in the foreground
  • Test with curl http://127.0.0.1:5000/
  1. Add supervision
  • Create hello.service, enable and start
  • Validate with systemctl status and journalctl -u hello.service
  1. Add the proxy
  • Install Nginx, add hello.conf, reload
  • Validate proxy path: curl http://127.0.0.1/api/
  1. Containerize
  • Write Dockerfile, build hello-app: local
  • Run container, validate with curl
  1. Unify operations
  • Decide whether systemd runs the app directly or manages docker run
  • Document start/stop/logs in helloctl
  1. Optional orchestration
  • If needed, deploy to a local cluster and port-forward for testing

At each step, prefer small, testable changes with clear rollbacks.

Local Pilot Plan

Goal: prove the stack on a single workstation with clear pass/fail.

Scope (keep it narrow):

  • One host, loopback traffic only
  • App on 127.0.0.1:5000, Nginx on :80 proxying /api/
  • Basic health endpoint at /healthz

Plan (about 60 to 90 minutes):

  1. App smoke test
  • Run /opt/app/app.py in foreground; curl / and /healthz
  • Success if both return 200 and expected bodies
  1. systemd supervision
  • Install hello.service; start; confirm restart on kill
  • Success if service restarts after pkill and logs are visible
  1. Nginx proxy
  • Configure hello.conf; curl /api/ and /healthz
  • Success if proxy paths return 200 and correct content
  1. Container check
  • Build hello-app: local; run; curl local endpoint
  • Success if containerized app behaves identically

Measures:

  • Latency p50 under 20 ms locally: for i in {1..20}; do time -p curl -s http://127.0.0.1/api/ >/dev/null; done
  • Memory footprint under 100 MB RSS for the app process

Exit criteria:

  • All success checks pass; failure analysis documented with command output snippets

This pilot is intentionally small, measurable, and easy to inspect locally before any broader rollout.

Conclusion

You mapped Linux architecture from kernel to user space, traced how control and data move through processes, filesystems, and networks, and built a practical HTTP service managed by systemd, fronted by Nginx, and packaged in Docker, with an option to run under Kubernetes. You also saw common failure points and how to observe and debug them. Next, extend the pilot by adding TLS in Nginx, structured logging, and a basic CI job that builds and runs the container locally. Keep changes small, validate each step, and capture what worked so the team can apply the pattern to the next service.

Article Quality Score

Reader usefulness 97%
  • check_circle Reader-ready guide
  • check_circle Practical examples included
  • check_circle Clean SEO article URL