The laptop is just a screen: a homelab you can work in from anywhere
I don’t develop on my laptop. It has Visual Studio Code, a password manager and a browser, and that is about it. Everything else, the tools, the repositories, the kubeconfigs, the shell setup, lives in my homelab. From anywhere with internet I open VS Code, log in, and I am at my desk.
This post is about the four pieces that make that work, why I ended up with this combination, and how to set it up yourself.
The connection between my laptop and all of that is a normal HTTPS connection. No VPN client to install, no SSH keys on the laptop, nothing to configure when I switch machines.
Coder: the desk I work at
Coder is an open source platform for development environments. You describe a workspace in Terraform, for example “a container with this image, this much CPU and memory, and a persistent home directory”. Coder builds that on my Kubernetes cluster when I ask for it, and tears it down when I don’t need it. The Coder docs cover the templates in detail.
Inside the workspace is everything I use for a day of work:
- The CLI tools: kubectl, Helm, Terraform, Ansible, Git, and so on.
- My repositories, checked out in the home directory that survives restarts.
- The VS Code extensions I need, installed in the container, not on my laptop.
- My Claude Code setup, with the plugins and skills I have collected. That is what wrote large parts of this website, in this very workspace.
On the laptop, VS Code has exactly one extension: the Coder extension. It lists my workspaces, I click one, and VS Code connects to it. Everything I type runs in the container in the homelab. The laptop is a screen and a keyboard, which has a few nice side effects:
- A new laptop is ready in ten minutes. Install VS Code, install one extension, log in.
- A lost laptop holds nothing. No source code, no kubeconfigs, no tokens. There’s nothing to wipe.
- Every workspace is the same. The image is the source of truth. If I break my environment, I delete the workspace and make a new one.
Kasm: for when I need a real screen
Some things don’t fit in a terminal. A browser session on the management network, a GUI tool, a quick look at something that only speaks HTML.
Kasm Workspaces streams a desktop to my browser. In my case that desktop is an Ubuntu virtual machine in the homelab, one of the VMs on the Proxmox nodes. Kasm itself runs on its own machine with Docker, separate from the Kubernetes cluster, so it keeps working while I am rebuilding that cluster. I open a tab, log in, and I am looking at the Ubuntu desktop as if I were sitting in front of it, with a browser and tools that live inside the homelab network. Close the tab and the VM keeps running, ready for next time.
Coder is where I work. Kasm is the window I open when I need a real desktop inside the lab.
Authentik: one login for everything
Coder sits behind Authentik. Authentik is an open source identity provider: one place that knows who I am and tells other applications so. Coder is configured to use it through OpenID Connect, so the Coder login page just says “Sign in with SSO”.
In practice: I open VS Code, the Coder extension sends me to the Authentik login, my password manager fills in the credentials, and I am in. One identity, one place to turn access off if a device goes missing, one place to see who logged in from where.
Authentik deserves its own post. It does a lot more than this, and some of it took me a while to get right.
Dotfiles: making a fresh workspace feel like home
A new workspace is a clean container. Clean is good for reproducibility and bad for comfort. No aliases, a plain prompt, default Git settings.
So the first thing that runs in a new workspace is my dotfiles installer. It sets up Zsh with a sensible prompt, around eighty aliases for Docker, Git, Kubernetes and Terraform, a tmux config, and Git defaults I would otherwise retype every time. The aliases that delete things ask before they do it, which has saved me more than once.
It’s one script, and it’s safe to run again. Existing files are backed up instead of overwritten. That means the same script works on a workspace, on the laptop when I do need a shell there, and on a fresh VM.
I will write a separate post about the dotfiles. They are the part of this setup people ask about most.
Setting this up yourself
This is the order I would do it in today. You need a Kubernetes cluster with
an ingress that can give you HTTPS, a machine with Docker for Kasm, and two
hostnames. I use coder.koper.dev and login.koper.dev as examples below;
use your own.
1. Install Coder on Kubernetes
Coder needs a PostgreSQL database. The Helm chart can install one for you for a first try, but for anything you want to keep, run PostgreSQL separately and give Coder the connection string as a Secret.
helm repo add coder-v2 https://helm.coder.com/v2
kubectl create namespace coder
kubectl -n coder create secret generic coder-db-url \
--from-literal=url="postgres://coder:<password>@postgres.coder.svc:5432/coder?sslmode=disable"
A minimal values.yaml:
coder:
env:
- name: CODER_ACCESS_URL
value: "https://coder.koper.dev"
- name: CODER_PG_CONNECTION_URL
valueFrom:
secretKeyRef:
name: coder-db-url
key: url
service:
type: ClusterIP
ingress:
enable: true
host: "coder.koper.dev"
tls:
enable: true
secretName: coder-tls
helm install coder coder-v2/coder -n coder -f values.yaml
Open the access URL, create the first admin user, and you have a Coder. Next, a template. Coder ships starter templates; the Kubernetes one gives every workspace a pod with a persistent volume for the home directory:
coder login https://coder.koper.dev
coder templates init --id kubernetes my-template
cd my-template
coder templates push
Edit main.tf in that folder to pick your image and resource sizes. The
image is where your CLI tools live, so put kubectl, Helm, Terraform and
whatever else you use in a Dockerfile and build it once.
2. Put Authentik in front of the login
In Authentik, create an OAuth2/OpenID provider (the
Authentik docs walk through it) with the redirect URI
https://coder.koper.dev/api/v2/users/oidc/callback, then an application
called coder that uses it. Copy the client ID and client secret into two
Kubernetes Secrets and add these environment variables to the Coder values:
- name: CODER_OIDC_ISSUER_URL
value: "https://login.koper.dev/application/o/coder/"
- name: CODER_OIDC_CLIENT_ID
valueFrom:
secretKeyRef:
name: coder-oidc-id
key: client-id
- name: CODER_OIDC_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: coder-oidc-secret
key: client-secret
- name: CODER_OIDC_SCOPES
value: "openid,profile,email,groups,offline_access"
- name: CODER_OIDC_USERNAME_FIELD
value: "preferred_username"
- name: CODER_OIDC_SIGN_IN_TEXT
value: "Sign in with SSO"
Run helm upgrade with the new values. The login page now offers “Sign in
with SSO”. Keep the built-in password login for the admin account until
you have confirmed SSO works, then turn it off.
3. Connect VS Code
On the laptop, install the “Coder” extension from the VS Code marketplace.
Run the command “Coder: Login”, enter https://coder.koper.dev, and finish
the login in the browser. The extension shows your workspaces in the
sidebar. Click one and VS Code opens a remote window into it.
Everything you install from here on, extensions included, goes into the workspace. The laptop stays empty.
4. Kasm and the Ubuntu VM
On the Docker machine, download the Kasm Workspaces release from kasmweb.com, unpack it and run the installer. The Kasm install guide has the current version number and requirements:
tar -xf kasm_release_*.tar.gz
sudo bash kasm_release/install.sh
It prints the admin credentials at the end. Log in, then add the VM: in
Workspaces, add a new workspace of type “Server”, point it at the Ubuntu
VM’s address, and choose how Kasm should connect to it. RDP works well
with xrdp installed on the VM. Give yourself access to the workspace, and
it appears on your Kasm dashboard. One click opens the VM’s desktop in the
browser tab.
Put Kasm behind the same HTTPS ingress or reverse proxy as everything else, so it is reachable from outside on a hostname of its own.
5. Dotfiles in every workspace
Coder has this built in. Add a dotfiles parameter to the template, or just run it by hand in a new workspace:
coder dotfiles https://gitlab.koper.dev/you/dotfiles.git
It clones the repository and runs the install script it finds there. A minute later the prompt, aliases and Git settings are yours. My dotfiles repository is public, and the next post is about what is in it.
How a day starts
- Open the laptop. Open VS Code.
- Click the Coder extension, pick a workspace. If it was stopped, it starts in about a minute.
- Log in through Authentik with the password manager, done.
- The terminal opens in my home directory, in my shell, with my aliases, in a container that has every tool I need and is already inside the homelab network.
From the outside this is one HTTPS connection to one hostname. From the inside I am sitting next to the servers.
What I would tell my past self
- Build the environment as an image, not by hand. If it is not in the Dockerfile or the dotfiles, it does not exist next week.
- One login, with a second factor, in front of everything. Adding Authentik early is much easier than adding it later.
- Keep the GUI escape hatch separate. Kasm on its own box means I still have a way in when Kubernetes is the thing that is broken.
- Write the dotfiles down. A repeatable shell setup is the difference between “new workspace” and “an hour of tweaking”.
What is next
Posts on the dotfiles and on Authentik are coming. The hardware all of this runs on is on the Homelab page, and there’s more about me on the About page. If there is a part of this setup you want to see in more detail, tell me.