Releases: wildshark/lab_os
Release list
LabOS
LabOS — Student Coding Environment
A single docker compose project that gives every student a browser-based
VS Code editor, a full LAMP stack, a reverse proxy with SSL support, a
standalone web terminal, and nine language runtimes — all running from
one integrated terminal, with no Dockerfile required anywhere in the
project.
What's included
| Service | Purpose | URL |
|---|---|---|
vscode |
Browser-based VS Code editor | http://localhost:8080 |
apache |
PHP web server | http://localhost:8081 |
mysql |
MySQL 8 database | internal only |
phpmyadmin |
Database admin UI | http://localhost:8082 |
nginx-proxy-manager |
Reverse proxy + free SSL | http://localhost:81 |
ttyd |
Standalone web terminal | http://localhost:7681 |
python-runner |
Python 3.12 | terminal only |
node-runner |
Node.js 20 | terminal only |
cpp-runner |
GCC / g++ | terminal only |
java-runner |
OpenJDK 21 | terminal only |
go-runner |
Go 1.22 | terminal only |
rust-runner |
Rust (stable) | terminal only |
ruby-runner |
Ruby 3.3 | terminal only |
dotnet-runner |
.NET SDK 8 | terminal only |
Architecture in one paragraph
The vscode container doesn't have language toolchains installed in it
directly. Instead, each language lives in its own lightweight sibling
container that just idles (sleep infinity), sharing the ./workspace
folder. vscode is given access to the host's Docker socket and a folder
of wrapper scripts (bin-wrappers/) placed first on its PATH. When a
student types python3 app.py, they're really running a wrapper that does
docker exec into the Python container — it feels local, but stays cleanly
isolated per language.
vscode root-equivalent
control over the host machine. This is a deliberate convenience tradeoff for
one trusted user on their own machine. Do not expose this setup to
multiple untrusted students or a shared/public server without redesigning
that part first.
Step-by-step setup
1. Prerequisites
docker --version
docker compose version # must be v2+Make sure these ports are free on your machine: 80, 443, 81, 7681, 8080, 8081, 8082.
2. Get the project
unzip student-coding-environment.zip -d labos
cd labos3. Configure secrets
cp .env.example .env
nano .envFill in at minimum:
CODE_SERVER_PASSWORD=...
MYSQL_ROOT_PASSWORD=...
MYSQL_DATABASE=...
MYSQL_USER=...
MYSQL_PASSWORD=...
Never commit .env — it's already in .gitignore.
4. Start everything
docker compose up -dFirst run pulls around 10 images (code-server, PHP, MySQL, phpMyAdmin, NPM,
ttyd, plus 8 language runtimes). Depending on your connection this can take
several minutes and several GB of disk space.
5. Confirm it's healthy
docker compose ps # everything should say "running" / "healthy"
docker compose logs -f vscode # Ctrl+C once it settles6. Open the services
| Service | URL | Login |
|---|---|---|
| Editor | http://localhost:8080 | password = CODE_SERVER_PASSWORD |
| PHP app | http://localhost:8081 | — |
| phpMyAdmin | http://localhost:8082 | MYSQL_USER / MYSQL_PASSWORD |
| NPM admin | http://localhost:81 | see step 7 |
| Web terminal | http://localhost:7681 | none by default — see step 7 |
7. First-login hardening
Nginx Proxy Manager: log in with admin@example.com / changeme,
then change both immediately — this default is public knowledge.
ttyd: ships with no authentication at all. Fine for local-only use.
If anyone else can reach this machine, edit the ttyd service's command:
in compose.yml to add basic auth before leaving it running:
command: ["ttyd", "-c", "student:changeme", "-p", "7681", "bash"]8. Test the language runtimes
In the code-server terminal (:8080 → Terminal → New Terminal):
python3 -c "print('python ok')"
node -e "console.log('node ok')"
g++ main.cpp -o main && cpp-exec ./main
java -version
go version
cargo --version
ruby -v
dotnet --versionFirst call to any language may pause a couple seconds while its container
starts — that's normal.
9. Everyday commands
docker compose stop # pause, keep data
docker compose start # resume
docker compose logs -f <service> # debug one service
docker compose down # stop + remove containers, data persists10. Full reset (destroys data)
docker compose down -v
rm -rf mysql_data code-server-data npm workspace/*Using the languages
# Python
python3 app.py
pip install requests
# Node.js
node index.js
npm install express
# C++ (compile normally, run via cpp-exec — see note below)
g++ main.cpp -o main
cpp-exec ./main
# Java
javac Main.java
java Main
# Go
go build -o app .
go-exec ./app
# Rust
cargo run
# Ruby
ruby script.rb
# C# / .NET
dotnet runWhy cpp-exec / go-exec exist
Compiling happens inside that language's own container, so the resulting
binary is linked against that container's libraries — not vscode's.
Running ./main directly from the code-server shell can fail with a library
mismatch. cpp-exec / go-exec run the binary back inside the container it
was built in, so it always works. Rust, Java, Ruby, Python, and Node don't
have this problem — their wrappers already run everything in the right place.
Project layout
.
├── compose.yml
├── .env.example
├── .gitignore
├── httpd.conf
├── README.md
├── bin-wrappers/
│ ├── python3, python, pip, pip3
│ ├── node, npm, npx
│ ├── g++, gcc, make, cmake, gdb, cpp-exec
│ ├── java, javac, jar
│ ├── go, go-exec
│ ├── cargo, rustc, rust-exec
│ ├── ruby, gem, bundle, irb
│ └── dotnet
├── workspace/ # student code (shared by all containers)
├── mysql_data/ # MySQL data (gitignored, created at runtime)
├── code-server-data/ # code-server settings/extensions (gitignored)
└── npm/ # Nginx Proxy Manager data + certs (gitignored)
Common tasks
Add a PHP extension: edit the apache service's command: block,
extend the docker-php-ext-install line.
Add another language: copy one of the *-runner service blocks, point it
at the right official image, add it to vscode's depends_on, and drop a
matching wrapper script into bin-wrappers/.
Known limitations
- Fixed container names (
student-env-python-runner, etc.) mean this is
designed for one instance per host. Running it multiple times side by
side (e.g. one per student on a shared server) will collide — that needs a
different architecture with per-student project names and no shared Docker
socket access. - Image size: with 9 language runtimes plus the rest of the stack, the
first pull is several gigabytes. - No build caching: since there's no Dockerfile, the
apacheand
vscodecontainers re-run their install commands on every restart, adding
some startup time.