Fahad KabirMechanical · CUET

Case study · Poridhi.io · 2025–26

GPU workspaces, from a machine per demand to a shared GPU server.

AI Studio gives each user a GPU workspace in the browser, with VS Code, Jupyter or marimo. I built its Next.js frontend, maintained the first version on AWS, and partnered with the team to move the platform onto the company’s own GPU server.

Next.jsKubernetes (K3s)HAMiTemporalPostgreSQL

ai.poridhi.io ↗ · Race a launch on the island →

Before · AWSone machine per demand
T4 · 16 GBnot divided

User 3's job takes the whole card. Users 1 and 2 run out of memory.

Up to 3 users, no isolation. Each could allocate the whole 16 GB, so one heavy job could starve the others.

After · own GPU serversessions share one card
RTX 4090, 48 GB6 × 8 GB, enforced

User 3's request past 8 GB is refused. Everyone else keeps running.

6 users, each capped at 8 GB. HAMi intercepts every GPU memory allocation inside a container and refuses anything past its slice, so the cap is enforced on the GPU, not left to good behaviour.

Both bars use the same scale. Before: up to three users on one 16 GB card, nothing stopping one of them taking all of it. After: six users on one 48 GB card, each held to 8 GB.

The first version

The first version ran on AWS. When no machine had room for a new session, a workflow booted a new EC2 GPU instance. The instance installed its tools and joined a Kubernetes (K3s) cluster before an editor could start. Up to three users then shared its T4 GPU, and nothing stopped one of them using all 16 GB.

request creates reads / writes state live status
AI Studio's first architecture on AWS The web app sends a launch request to the Go API server, which starts a Temporal workflow. The workflow records machine state in PostgreSQL and launches an EC2 GPU instance on AWS, which joins a K3s cluster. The API server deploys the IDE container and a storage sidecar onto it; the sidecar mounts per-user storage on S3 through JuiceFS. Ingress and a secure tunnel return the workspace URL to the browser. BROWSER API SERVER · GO STATE AWS K3S CLUSTER AI Studio web apppick editor · launch API serverauth · gateway · status Temporallaunch + cleanup flows PostgreSQLmachine state Redisqueue · credits EC2 GPU instancespot g4dn · 1× T4, 16 GBjoins as a GPU node IDE container storage sidecar × N, one per demand IngressNginx + tunnel Per-user storageJuiceFS on S3 live status start launch machine state launch deploy IDE + sidecar mount files serves workspace URL back to the user 1 2

1 A whole GPU machine boots for a new demand, installs its tools and joins the cluster: 5–10 minutes before the editor can even start.

2 Up to 3 users share one T4, and nothing stops one of them using all 16 GB.

  1. AI Studio web appYou choose an editor and press launch. Status streams back live.
  2. API server (Go)Checks who you are and starts a launch workflow.
  3. TemporalRuns the launch and the cleanup. Machine state is kept in PostgreSQL; Redis queues tasks and credits.
  4. EC2 GPU instance, on AWSA spot g4dn (1× T4, 16 GB) boots and joins the K3s cluster. 5–10 minutes. Up to 3 users share it, with no isolation.
  5. IDE container + storage sidecarDeployed onto the instance. The sidecar mounts your files from S3 through JuiceFS.
  6. Ingress and secure tunnelReturn the workspace URL to your browser.

Why move

  • A new machine took 5–10 minutes to be ready, before the editor even started.
  • No memory isolation on the GPU: one heavy job could starve everyone else on the card.
  • Spot instances could be interrupted in the middle of a session.
  • Every instance cost money by the hour, even when it sat half used.
  • One instance type, one region, one GPU model.

The move

The platform moved to the company’s own GPU server: one 48 GB card, split by HAMi into six 8 GB slices. HAMi sits inside each container and refuses any GPU memory allocation past its slice, so six people can share the card without touching each other’s memory. Time-slicing alone, Kubernetes’ built-in way to share a GPU, can’t enforce that.

First version (AWS)GPU server
A new sessionmay wait for a machine to boottakes a ready, pre-started pod
Sharing a GPUup to 3 users, no isolation6 users, 8 GB each, enforced
Machine lifecyclecreate, use, terminateassign, use, release
Your filesJuiceFS on S3a volume on the server

How a launch works now

The design the migration followed. Drawn from the post-prototype design document. Which details reached production is not yet confirmed.

Workspace launch sequence on the company GPU server The web app asks the API server to launch an editor. The API starts a Temporal workflow, which checks and deducts credits and finds a free pre-started pod in PostgreSQL, assigns the pod and starts the editor in it on the GPU server, verifies it is ready, saves the session and reports the workspace ready. The workflow then stays open: the web app sends a heartbeat every five minutes, and when the timer runs out, the user stops, or the session goes idle, the workflow stops the editor, releases the pod, ends the session and refunds unused credits. Web app API server Temporal PostgreSQL GPU serverK3s · HAMi · pod pool LAUNCH · THE DESIGN EXPECTED SECONDS launch: editor, duration start the workflow check, deduct credits find a free pod assign the pod · start the editor verify it is ready save the session workspace ready (status streams live) SESSION · THE WORKFLOW STAYS OPEN heartbeat, every 5 minutes waits for: timer · heartbeat · stop stop the editor · release the pod end session · refund unused

The slow step from the first version is gone: pods are already running on the GPU server with their 8 GB slice, so a launch only assigns one and starts the editor inside it. The same workflow then watches the session until the timer ends, the user stops it, or the heartbeat stops.

  1. Web app → API serverLaunch with an editor and a duration.
  2. API server → TemporalStarts one workflow for the whole session.
  3. Temporal ↔ PostgreSQLChecks and deducts credits, then finds a free pre-started pod.
  4. Temporal → GPU serverAssigns the pod, starts the editor inside it, and checks it's ready.
  5. Workspace readyThe session is saved and the link returns to the browser.
  6. While you workThe browser sends a heartbeat every 5 minutes; the workflow waits for the timer, a stop, or silence.
  7. At the endThe editor stops, the pod goes back to the pool, unused credits are refunded.

What I did

BuiltThe Next.js frontend where users choose their editor, memory and GPU for each session.
With the teamThe move from AWS to the company GPU server. Bringing the backend services, database and workflow engine up on the new cluster, each with its own access controls.
MaintainedThe first version on AWS, which I kept running and extended.

Result

10–15 → 3–5minutes to launch
6 × 8 GBusers per 48 GB GPU

Launch times as seen in the workflow history, not a formal benchmark. The design expected launches in seconds; what still takes minutes in practice isn’t written up here yet.

Open questions in the design

  • Files live on one server’s volume, so a user moved to another server wouldn’t find them there.
  • A fixed pool of slices means a full server has to say “no capacity”, or queue, or fall back to the cloud.
  • Both the database and HAMi know which slices are in use; keeping two records in step is its own problem.