diff --git a/labs/lab9-solution/project/Makefile b/labs/lab9-solution/project/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..3d2da20b6b3bb5ee7e2c543e7a9af72d71d97780
--- /dev/null
+++ b/labs/lab9-solution/project/Makefile
@@ -0,0 +1,29 @@
+help:
+	@echo "build      build the app"
+	@echo "deploy     deploy the app"
+	@echo "run        run the app"
+	@echo "logs       display the app's logs"
+	@echo "clean      clean the app"
+
+build:
+	docker buildx build -t md2html md2html
+	docker buildx build -t logger logger
+	docker buildx build -t web web
+	docker network create web-net
+	docker network create proc-net
+
+deploy:
+	docker run -d -p 2000:12345 -h logger --network proc-net --name logger --rm logger
+	docker run -d -p 3000:80 -h web --network web-net --name web --rm --mount type=bind,src=$$(pwd)/data,dst=/data web
+
+logs:
+	curl localhost:2000/logs
+
+run:
+	docker run -h md2html --network proc-net --name md2html --rm --mount type=bind,src=$$(pwd)/data,dst=/data md2html
+
+clean:
+	docker rm -f web logger
+	docker image rm -f md2html logger
+	docker network rm -f proc-net web-net
+	rm -f data/output/*
diff --git a/labs/lab9-solution/project/data/input/aliens.md b/labs/lab9-solution/project/data/input/aliens.md
new file mode 100644
index 0000000000000000000000000000000000000000..a1b494ea24261cb7c86d610414f43fc07c98db6d
--- /dev/null
+++ b/labs/lab9-solution/project/data/input/aliens.md
@@ -0,0 +1,27 @@
+# They Came at 2:03 AM
+
+It started with a **low hum**, like a refrigerator from across the galaxy.  
+*Martin* had been up late, coding in his garage, when the noise made his keyboard rattle.
+
+He stepped outside, barefoot, wrapped in a hoodie that still smelled like *burnt coffee*.  
+Above the cornfield, something was hovering. Not spinning. Not blinking. Just... *waiting*.
+
+He reached for his phone. **No signal.**  
+No time to post it, tweet it, or even record it.
+
+The object descended a few feet - smooth and silent, shaped like a *teardrop made of dark glass*.
+
+A hatch hissed open. Light poured out, **too bright** and **too quiet**.  
+Then *they* stepped out. Tall. Unmoving. Featureless.
+
+Martin did what no one in every alien movie ever seemed to do:
+
+> He sat down and waved.
+
+**"Hi,"** he said. *"I'm Martin. I suck at sleeping."*
+
+The taller one tilted its head.  
+A thin beam projected a sentence into the dirt:
+
+```text
+ARE YOU THE ONE WHO BUILT THE SIGNAL?
diff --git a/labs/lab9-solution/project/data/input/cat.md b/labs/lab9-solution/project/data/input/cat.md
new file mode 100644
index 0000000000000000000000000000000000000000..15f970be6e7cf71eef361d3370ed84bdde296435
--- /dev/null
+++ b/labs/lab9-solution/project/data/input/cat.md
@@ -0,0 +1,18 @@
+# The Cat Who Understood Time
+
+Milo wasn't an ordinary cat. Sure, he looked the part-fuzzy ears, twitchy tail, eyes like marbles in moonlight-but there was something about the way he stared at clocks.
+
+Ever since Ava adopted him from the dusty shelter near the train station, she noticed his peculiar habits. He'd perch beneath her antique grandfather clock at **exactly 11:11 AM**, every day. When the chimes rang, he'd close his eyes, purr once, and vanish into the shadows of the hallway.
+
+One rainy Tuesday, Ava followed him. He slipped under the kitchen cupboard, and to her amazement, a soft shimmer of blue light outlined the floorboards. She reached forward, hand trembling.
+
+> "What you think is lost," she remembered her grandmother once saying,  
+> "is sometimes only waiting to be found-just elsewhere in time."
+
+When she pushed open the loose plank, she found a spiral staircase leading *down*. Milo was waiting, tail flicking, as if to say: *Finally.*
+
+At the bottom, glowing softly in a chamber of forgotten gears, stood a **time machine**. It was labeled in scratchy etching: `Property of T. Whiskers`.
+
+Ava turned to Milo. He blinked once. She understood.
+
+They weren't going back upstairs. Not yet.
diff --git a/labs/lab9-solution/project/data/input/plan9.md b/labs/lab9-solution/project/data/input/plan9.md
new file mode 100644
index 0000000000000000000000000000000000000000..dbfae4d2778885689bb1f2231948def24f325d27
--- /dev/null
+++ b/labs/lab9-solution/project/data/input/plan9.md
@@ -0,0 +1,20 @@
+# Plan 9 from Bell Labs
+
+**Plan 9** is an operating system developed by **Bell Labs** as the conceptual successor to Unix. Designed in the late 1980s and early 1990s, it was created by some of the same minds behind Unix: *Ken Thompson*, *Dennis Ritchie*, and *Rob Pike*, among others.
+
+Unlike traditional operating systems, Plan 9 treats **everything as a file**, and more importantly, it **exports resources as files** via its powerful `9P` protocol. This makes **distributed computing** a natural part of the environment, not an afterthought.
+
+## Key Concepts
+
+- *Everything is a file* - including GUI elements and network interfaces.
+- *Namespaces* are customizable per process.
+- *9P Protocol* allows seamless resource sharing between systems.
+- *Union directories* let you overlay file systems cleanly.
+
+## Example: Mounting a Window
+
+Here's how you'd mount a remote graphical window server:
+
+```sh
+mount -c /srv/graphics /dev/draw
+```
diff --git a/labs/lab9-solution/project/logger/Dockerfile b/labs/lab9-solution/project/logger/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..5fb4ce2a2ee3ee8d54c8eb7574fb76395cea76c3
--- /dev/null
+++ b/labs/lab9-solution/project/logger/Dockerfile
@@ -0,0 +1,9 @@
+FROM alpine:3.21
+
+RUN apk update
+RUN apk add python3 py3-flask
+
+COPY logger_api.py /
+WORKDIR /
+
+ENTRYPOINT ["python", "logger_api.py"]
\ No newline at end of file
diff --git a/labs/lab9-solution/project/logger/logger_api.py b/labs/lab9-solution/project/logger/logger_api.py
new file mode 100644
index 0000000000000000000000000000000000000000..68b77d8e4d79b08a3a2b804c0ed14bf0213b2ef0
--- /dev/null
+++ b/labs/lab9-solution/project/logger/logger_api.py
@@ -0,0 +1,26 @@
+from flask import Flask, request, jsonify
+from flask import Response
+
+logs = []
+
+app = Flask(__name__)
+
+@app.route('/log', methods=['POST'])
+def receive_message():
+    data = request.get_json()
+
+    if not data or 'message' not in data:
+        return jsonify({'error': 'Missing "message" field'}), 400
+
+    msg = data['message']
+    print(f"DEBUG: message: {msg}")
+    logs.append(msg) 
+
+    return Response(status=200)
+
+@app.route('/logs', methods=['GET'])
+def dump_messages():
+    return jsonify(logs), 200
+
+if __name__ == '__main__':
+    app.run(host='0.0.0.0', port=12345)
diff --git a/labs/lab9-solution/project/md2html/Dockerfile b/labs/lab9-solution/project/md2html/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..15676707aa546d6c4993485ae847004e131f248c
--- /dev/null
+++ b/labs/lab9-solution/project/md2html/Dockerfile
@@ -0,0 +1,19 @@
+FROM ubuntu:24.04
+
+WORKDIR /build
+COPY Makefile /build
+COPY md2html.c /build
+
+RUN apt-get update -y
+RUN apt-get install -y gcc make libcmark-dev curl \
+    && make \
+    && mv md2html /usr/bin \
+    && rm -rf /build \
+    && apt-get remove -y gcc make libcmark-dev \
+    && apt autoremove -y
+RUN apt-get install -y libcmark0.30.2
+
+WORKDIR /
+COPY --chmod=755 convert.sh /
+
+CMD ["/convert.sh"]
\ No newline at end of file
diff --git a/labs/lab9-solution/project/md2html/Makefile b/labs/lab9-solution/project/md2html/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..42c0032e357cdb32acb3534b036bcc9c91002fec
--- /dev/null
+++ b/labs/lab9-solution/project/md2html/Makefile
@@ -0,0 +1,5 @@
+md2html: md2html.c
+	gcc $< -o $@ -lcmark
+
+clean:
+	rm -f md2html
diff --git a/labs/lab9-solution/project/md2html/convert.sh b/labs/lab9-solution/project/md2html/convert.sh
new file mode 100644
index 0000000000000000000000000000000000000000..24ec725fa180a66ba8c3cc228a58bd6edb35b23f
--- /dev/null
+++ b/labs/lab9-solution/project/md2html/convert.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+for i in /data/input/*.md; do
+    f=$(basename $i).html
+    md2html $i > /data/output/$f
+    curl -X POST http://logger:12345/log -H "Content-Type: application/json" -d "{\"message\": \"$f\"}"
+done
\ No newline at end of file
diff --git a/labs/lab9-solution/project/md2html/md2html.c b/labs/lab9-solution/project/md2html/md2html.c
new file mode 100644
index 0000000000000000000000000000000000000000..a6cfc7c14319ad01d07df9b6e5e98eaeeb9081d7
--- /dev/null
+++ b/labs/lab9-solution/project/md2html/md2html.c
@@ -0,0 +1,33 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <cmark.h>
+
+int main(int argc, char *argv[]) {
+    if (argc != 2) {
+        fprintf(stderr, "Usage: %s <markdown-file>\n", argv[0]);
+        return 1;
+    }
+
+    // Open and read the Markdown file
+    FILE *fp = fopen(argv[1], "rb");
+    if (!fp) {
+        perror("fopen");
+        return 1;
+    }
+
+    fseek(fp, 0, SEEK_END);
+    long len = ftell(fp);
+    fseek(fp, 0, SEEK_SET);
+    char *md = malloc(len + 1);
+    fread(md, 1, len, fp);
+    md[len] = '\0';
+    fclose(fp);
+
+    // Convert to HTML
+    char *html = cmark_markdown_to_html(md, len, CMARK_OPT_DEFAULT);
+    printf("%s\n", html);
+
+    free(md);
+    free(html);
+    return 0;
+}
diff --git a/labs/lab9-solution/project/web/Dockerfile b/labs/lab9-solution/project/web/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..b88c847ccbf03c6b2a4f3065014aceedde91da56
--- /dev/null
+++ b/labs/lab9-solution/project/web/Dockerfile
@@ -0,0 +1,8 @@
+FROM debian:bookworm
+
+RUN apt-get update -y
+RUN apt-get install -y nginx
+
+COPY default /etc/nginx/sites-enabled
+
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/labs/lab9-solution/project/web/default b/labs/lab9-solution/project/web/default
new file mode 100644
index 0000000000000000000000000000000000000000..e7712e539008962bd3d705a1301683ee9307bb36
--- /dev/null
+++ b/labs/lab9-solution/project/web/default
@@ -0,0 +1,91 @@
+##
+# You should look at the following URL's in order to grasp a solid understanding
+# of Nginx configuration files in order to fully unleash the power of Nginx.
+# https://www.nginx.com/resources/wiki/start/
+# https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
+# https://wiki.debian.org/Nginx/DirectoryStructure
+#
+# In most cases, administrators will remove this file from sites-enabled/ and
+# leave it as reference inside of sites-available where it will continue to be
+# updated by the nginx packaging team.
+#
+# This file will automatically load configuration files provided by other
+# applications, such as Drupal or Wordpress. These applications will be made
+# available underneath a path with that package name, such as /drupal8.
+#
+# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
+##
+
+# Default server configuration
+#
+server {
+	listen 80 default_server;
+	listen [::]:80 default_server;
+
+	# SSL configuration
+	#
+	# listen 443 ssl default_server;
+	# listen [::]:443 ssl default_server;
+	#
+	# Note: You should disable gzip for SSL traffic.
+	# See: https://bugs.debian.org/773332
+	#
+	# Read up on ssl_ciphers to ensure a secure configuration.
+	# See: https://bugs.debian.org/765782
+	#
+	# Self signed certs generated by the ssl-cert package
+	# Don't use them in a production server!
+	#
+	# include snippets/snakeoil.conf;
+
+	root /data/output;
+
+	# Add index.php to the list if you are using PHP
+	index index.html index.htm index.nginx-debian.html;
+
+	server_name _;
+
+	location / {
+		# First attempt to serve request as file, then
+		# as directory, then fall back to displaying a 404.
+		try_files $uri $uri/ =404;
+	}
+
+	# pass PHP scripts to FastCGI server
+	#
+	#location ~ \.php$ {
+	#	include snippets/fastcgi-php.conf;
+	#
+	#	# With php-fpm (or other unix sockets):
+	#	fastcgi_pass unix:/run/php/php7.4-fpm.sock;
+	#	# With php-cgi (or other tcp sockets):
+	#	fastcgi_pass 127.0.0.1:9000;
+	#}
+
+	# deny access to .htaccess files, if Apache's document root
+	# concurs with nginx's one
+	#
+	#location ~ /\.ht {
+	#	deny all;
+	#}
+}
+
+
+# Virtual Host configuration for example.com
+#
+# You can move that to a different file under sites-available/ and symlink that
+# to sites-enabled/ to enable it.
+#
+#server {
+#	listen 80;
+#	listen [::]:80;
+#
+#	server_name example.com;
+#
+#	root /var/www/example.com;
+#	index index.html;
+#
+#	location / {
+#		try_files $uri $uri/ =404;
+#	}
+#}