GitHub Codespaces Transition Planning
Why We're Moving Away From KASM
KASM gives students a full cloud Ubuntu desktop with VS Code in the browser. It works well, but it requires:
- A dedicated AWS EC2 instance (or cluster) running 24/7
- Manual user provisioning tied to our Flask API
- Ongoing KASM license/infrastructure costs
- Our own ops burden (updates, disk space, container restarts)
GitHub Codespaces gives us the same core value — browser-accessible VS Code, preconfigured environment, works on any machine — without managing any infrastructure. GitHub handles the VMs. We define the environment in code.
What Codespaces Gives Us (vs. KASM)
|
KASM |
GitHub Codespaces |
| VS Code in browser |
Yes |
Yes |
| Preconfigured environment |
Manual setup |
.devcontainer/ in repo |
| Infrastructure managed by us |
Yes (AWS) |
No (GitHub) |
| Cost model |
Always-on EC2 |
Pay per active compute-hours |
| Auth/provisioning |
Custom Flask API integration |
GitHub account = access |
| Per-repo isolation |
No (shared VM) |
Yes (each repo gets its own codespace) |
| Idle shutdown |
Manual |
Automatic (configurable timeout) |
| Storage |
Persistent KASM workspace |
Persistent per-codespace (up to 30 days idle) |
The biggest operational win: no more KASM user provisioning code in Flask, no AWS EC2 bill for the KASM cluster, and students use their existing GitHub accounts.
The Three Repos and What Each Codespace Needs
1. Flask (Python/Flask — port 8587)
Already has a .devcontainer/ directory, but it's an old Microsoft template configured for Node/TypeScript. It needs to be replaced with a proper Python/Flask config.
What the devcontainer needs to do:
- Use a Python 3.x base image
- Install dependencies from
requirements.txt (Flask, SQLAlchemy, PyJWT, boto3, etc.)
- Start the Flask dev server on port 8587 (forwarded automatically by Codespaces)
- Provide VS Code extensions: Python, Pylance, SQLite viewer, REST client for testing endpoints
Minimal .devcontainer/devcontainer.json structure:
{
"name": "Flask Backend",
"image": "mcr.microsoft.com/devcontainers/python:3.11",
"postCreateCommand": "pip install -r requirements.txt",
"forwardPorts": [8587],
"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"ms-python.pylance",
"qwtel.sqlite-viewer",
"humao.rest-client"
]
}
}
}
Important: The .env file with KASM_API_KEY, SECRET_KEY, AWS_* etc. should never go in the repo. Use Codespaces secrets — they're set per-repo at the org or user level and are injected as environment variables automatically when a codespace starts.
2. Spring (Java 21 / Spring Boot — port 8585)
No devcontainer exists yet. Spring Boot with Maven is well-supported by the standard Java devcontainer image.
What the devcontainer needs to do:
- Use a Java 21 + Maven base image
- Run
mvn install or ./mvnw install on create to pre-download dependencies
- Forward port 8585
- Provide VS Code extensions: Extension Pack for Java, Spring Boot Dashboard
Minimal .devcontainer/devcontainer.json structure:
{
"name": "Spring Backend",
"image": "mcr.microsoft.com/devcontainers/java:21-bullseye",
"postCreateCommand": "./mvnw -q dependency:resolve",
"forwardPorts": [8585],
"customizations": {
"vscode": {
"extensions": [
"vscjava.vscode-java-pack",
"vmware.vscode-spring-boot",
"vscjava.vscode-spring-initializr"
]
}
}
}
Note: Spring uses SQLite locally (no MySQL needed in the codespace). The local SQLite path in local.properties already handles this — no changes required for the dev experience.
3. Pages (Jekyll — port 4500)
Pages is a Jekyll site. It also needs Python for the notebook/DOCX conversion scripts (make convert).
What the devcontainer needs to do:
- Use an image with both Ruby (for Jekyll/Bundler) and Python 3 (for conversion scripts)
- Run
bundle install and pip install -r requirements.txt on create
- Forward port 4500
- Provide VS Code extensions: Markdown All in One, Liquid syntax highlighting
Minimal .devcontainer/devcontainer.json structure:
{
"name": "Pages (Jekyll)",
"image": "mcr.microsoft.com/devcontainers/ruby:3.2",
"postCreateCommand": "bundle install && pip3 install -r requirements.txt",
"forwardPorts": [4500],
"customizations": {
"vscode": {
"extensions": [
"yzhang.markdown-all-in-one",
"sibiraj-s.vscode-scss-formatter",
"Shopify.theme-check-vscode"
]
}
}
}
The existing make workflow (make serve, make dev, make stop) works as-is inside a codespace — students just open a terminal and run make.
Transition Pathway
Phase 1 — Set Up devcontainers (no user-facing change yet)
- Replace the Flask
.devcontainer/ with the Python config above
- Add a
.devcontainer/ to the Spring repo
- Add a
.devcontainer/ to the Pages repo
- Test each: open a codespace, verify the server starts, verify port forwarding works, verify the VS Code extensions load
This is purely additive — nothing breaks for existing KASM users or local dev setups.
Phase 2 — Codespaces secrets for environment variables
Set up the following as Codespaces secrets at the org level so any student's codespace inherits them automatically:
| Secret name |
Used by |
What it is |
FLASK_SECRET_KEY |
Flask |
JWT encryption key |
KASM_API_KEY |
Flask |
(can be removed once KASM is off) |
KASM_API_KEY_SECRET |
Flask |
(can be removed once KASM is off) |
AWS_ACCESS_KEY_ID |
Flask, DB Automator |
AWS credentials for Aurora/S3 |
AWS_SECRET_ACCESS_KEY |
Flask, DB Automator |
AWS credentials |
Students working locally still use .env as before. Codespaces just picks these up from the org secrets.
Phase 3 — Communicate to students
Update the KASM setup guide in pages-tracking/_posts/Foundation/B-tools_and_equipment/2025-04-15-tools_setup-kasm.md with a Codespaces equivalent. The student flow becomes:
- Accept the GitHub org invite (they already need this for the repo)
- Go to the repo on GitHub → click Code → Codespaces → Create codespace on main
- Wait ~2 minutes for the environment to build
- VS Code opens in the browser — the server starts automatically or they run
make / ./mvnw spring-boot:run
No AWS, no KASM account, no SSH keys, no local install required.
Phase 4 — Decommission KASM
Once the devcontainers are stable and students are using Codespaces:
- Remove the KASM provisioning code from Flask (
model/kasm.py, the /kasm_users and /delete_user routes in main.py)
- Remove the
kasmServerNeeded field from Spring's Person entity (and the DB migration)
- Remove
KASM_SERVER, KASM_API_KEY, KASM_API_KEY_SECRET from env/config
- Shut down the AWS KASM instance
- Archive
pages-tracking/scripts/kasm_api_funcs.py
This is the point where the AWS cost actually disappears.
Cost Comparison
KASM (current)
- EC2 instance(s): always-on, billed by the hour regardless of student activity
- You own the ops: upgrades, disk, container health, user provisioning
Codespaces
- Free tier: 120 core-hours/month per GitHub user (resets monthly)
- A 2-core codespace costs 2 core-hours per active hour
- That's 60 hours/month of active coding per student for free
- Students who hit the limit can pay
$0.18/hr for 2-core themselves, or the org can buy Codespaces hours ($0.18/core-hour billed to the org)
- Idle codespaces auto-stop (default 30 min timeout — configurable)
- No always-on cost
For a class of 30 students doing maybe 10–15 hours/week of active coding, the free tier alone likely covers most usage. The org can set a spending limit to cap any overages.
Things to Watch Out For
Database state between sessions
KASM had a persistent disk. Codespaces also persist for up to 30 days of inactivity, but if a student deletes their codespace (or it's auto-deleted after 30 days idle), the SQLite database is gone. For Flask and Spring, this is fine for student work — they're developing against a local SQLite dev DB, not production. Make sure students know not to put anything important in the codespace's SQLite that isn't committed to the repo or exported.
Port visibility
By default, forwarded ports in Codespaces are private (only the codespace owner can access them). If you want students to share a live preview of their Flask/Spring app with a teacher or peer for code review, they can make the port public temporarily from the Ports tab in VS Code. This is fine for short demos but should not be left public.
Startup time
The first time a codespace builds for a repo, it runs postCreateCommand (installs all dependencies). This takes 1–3 minutes. After that, subsequent opens are fast (~15 seconds) because the environment is cached. If the devcontainer.json changes, the cache is invalidated and it rebuilds.
Org access control
Codespaces access is tied to repo access. If a student has read access to the repo, they can open a codespace for it. This is exactly what we want — the same permission model we already use for GitHub.
The DB Automator
The tracking-database-automator connects to the Docker socket and monitors the Flask/Spring containers. It does not need a Codespaces devcontainer — it runs on the production server. Students don't need access to it.
Summary Checklist
GitHub Codespaces Transition Planning
Why We're Moving Away From KASM
KASM gives students a full cloud Ubuntu desktop with VS Code in the browser. It works well, but it requires:
GitHub Codespaces gives us the same core value — browser-accessible VS Code, preconfigured environment, works on any machine — without managing any infrastructure. GitHub handles the VMs. We define the environment in code.
What Codespaces Gives Us (vs. KASM)
.devcontainer/in repoThe biggest operational win: no more KASM user provisioning code in Flask, no AWS EC2 bill for the KASM cluster, and students use their existing GitHub accounts.
The Three Repos and What Each Codespace Needs
1. Flask (Python/Flask — port 8587)
Already has a
.devcontainer/directory, but it's an old Microsoft template configured for Node/TypeScript. It needs to be replaced with a proper Python/Flask config.What the devcontainer needs to do:
requirements.txt(Flask, SQLAlchemy, PyJWT, boto3, etc.)Minimal
.devcontainer/devcontainer.jsonstructure:{ "name": "Flask Backend", "image": "mcr.microsoft.com/devcontainers/python:3.11", "postCreateCommand": "pip install -r requirements.txt", "forwardPorts": [8587], "customizations": { "vscode": { "extensions": [ "ms-python.python", "ms-python.pylance", "qwtel.sqlite-viewer", "humao.rest-client" ] } } }Important: The
.envfile withKASM_API_KEY,SECRET_KEY,AWS_*etc. should never go in the repo. Use Codespaces secrets — they're set per-repo at the org or user level and are injected as environment variables automatically when a codespace starts.2. Spring (Java 21 / Spring Boot — port 8585)
No devcontainer exists yet. Spring Boot with Maven is well-supported by the standard Java devcontainer image.
What the devcontainer needs to do:
mvn installor./mvnw installon create to pre-download dependenciesMinimal
.devcontainer/devcontainer.jsonstructure:{ "name": "Spring Backend", "image": "mcr.microsoft.com/devcontainers/java:21-bullseye", "postCreateCommand": "./mvnw -q dependency:resolve", "forwardPorts": [8585], "customizations": { "vscode": { "extensions": [ "vscjava.vscode-java-pack", "vmware.vscode-spring-boot", "vscjava.vscode-spring-initializr" ] } } }Note: Spring uses SQLite locally (no MySQL needed in the codespace). The local SQLite path in
local.propertiesalready handles this — no changes required for the dev experience.3. Pages (Jekyll — port 4500)
Pages is a Jekyll site. It also needs Python for the notebook/DOCX conversion scripts (
make convert).What the devcontainer needs to do:
bundle installandpip install -r requirements.txton createMinimal
.devcontainer/devcontainer.jsonstructure:{ "name": "Pages (Jekyll)", "image": "mcr.microsoft.com/devcontainers/ruby:3.2", "postCreateCommand": "bundle install && pip3 install -r requirements.txt", "forwardPorts": [4500], "customizations": { "vscode": { "extensions": [ "yzhang.markdown-all-in-one", "sibiraj-s.vscode-scss-formatter", "Shopify.theme-check-vscode" ] } } }The existing
makeworkflow (make serve,make dev,make stop) works as-is inside a codespace — students just open a terminal and runmake.Transition Pathway
Phase 1 — Set Up devcontainers (no user-facing change yet)
.devcontainer/with the Python config above.devcontainer/to the Spring repo.devcontainer/to the Pages repoThis is purely additive — nothing breaks for existing KASM users or local dev setups.
Phase 2 — Codespaces secrets for environment variables
Set up the following as Codespaces secrets at the org level so any student's codespace inherits them automatically:
FLASK_SECRET_KEYKASM_API_KEYKASM_API_KEY_SECRETAWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYStudents working locally still use
.envas before. Codespaces just picks these up from the org secrets.Phase 3 — Communicate to students
Update the KASM setup guide in pages-tracking/_posts/Foundation/B-tools_and_equipment/2025-04-15-tools_setup-kasm.md with a Codespaces equivalent. The student flow becomes:
make/./mvnw spring-boot:runNo AWS, no KASM account, no SSH keys, no local install required.
Phase 4 — Decommission KASM
Once the devcontainers are stable and students are using Codespaces:
model/kasm.py, the/kasm_usersand/delete_userroutes inmain.py)kasmServerNeededfield from Spring'sPersonentity (and the DB migration)KASM_SERVER,KASM_API_KEY,KASM_API_KEY_SECRETfrom env/configpages-tracking/scripts/kasm_api_funcs.pyThis is the point where the AWS cost actually disappears.
Cost Comparison
KASM (current)
Codespaces
$0.18/hr for 2-core themselves, or the org can buy Codespaces hours ($0.18/core-hour billed to the org)For a class of 30 students doing maybe 10–15 hours/week of active coding, the free tier alone likely covers most usage. The org can set a spending limit to cap any overages.
Things to Watch Out For
Database state between sessions
KASM had a persistent disk. Codespaces also persist for up to 30 days of inactivity, but if a student deletes their codespace (or it's auto-deleted after 30 days idle), the SQLite database is gone. For Flask and Spring, this is fine for student work — they're developing against a local SQLite dev DB, not production. Make sure students know not to put anything important in the codespace's SQLite that isn't committed to the repo or exported.
Port visibility
By default, forwarded ports in Codespaces are private (only the codespace owner can access them). If you want students to share a live preview of their Flask/Spring app with a teacher or peer for code review, they can make the port public temporarily from the Ports tab in VS Code. This is fine for short demos but should not be left public.
Startup time
The first time a codespace builds for a repo, it runs
postCreateCommand(installs all dependencies). This takes 1–3 minutes. After that, subsequent opens are fast (~15 seconds) because the environment is cached. If thedevcontainer.jsonchanges, the cache is invalidated and it rebuilds.Org access control
Codespaces access is tied to repo access. If a student has read access to the repo, they can open a codespace for it. This is exactly what we want — the same permission model we already use for GitHub.
The DB Automator
The tracking-database-automator connects to the Docker socket and monitors the Flask/Spring containers. It does not need a Codespaces devcontainer — it runs on the production server. Students don't need access to it.
Summary Checklist
flask-tracking/.devcontainer/with Python 3.11 config.devcontainer/tospring-tracking/.devcontainer/topages-tracking/