Published: Apr 24, 2025 by Isaac Johnson
After looking into Coder, Google results suggested I check out Gitpod as well. Like Coder and Github Codespaces, Gitpod aims to grant developers dev environments with a built in code-server.
But unlike the others, Gitpod doesn’t use Kubernetes or Docker and instead uses KVM for kernel level virtualization. There are some positives and negatives with this approach.
Let’s dig in and see how Gitpod might work for development environments.
Gitpod Setup
We can find Gitpod at Gitpod.io. Heading to Pricing, we can see there is a “Free” tier for individuals.
Clicking the free option prompts me to continue with the IdP of my choosing
I need to now make an Organization
The next parts of the form were just glorified sales pitches so I did a quick next-next on them.
I can now see a Projects page with a getting started guide
I watched the video and they said the desktop client doesn’t need Docker Desktop to run the devcontainer which sparked my interest a bit more.
The Runner Setup suggests using Apple Silicon (latest Macs) or cloud. This is an easy choice as there is no Apple Silicon in my house.
With Cloud, they basically have “use Linux” or AWS, for now.
At least with the Linux, they do list out the requirements which is handy. In my eyes, this covers just about every other cloud.
When I click continue, I get the specific steps to run in Linux (this would be an easy Ansible Playbook)
The first gotcha I had was that it needed KVM modules installed
This is not Keyboard-Video-Mouse, mind you. This is the old school Linux hypervisor.
$ sudo apt update
$ sudo apt -y install bridge-utils cpu-checker libvirt-clients libvirt-daemon qemu qemu-kvm
But despite it showing okay
builder@bosgamerz7:~$ sudo /usr/sbin/kvm-ok
INFO: /dev/kvm does not exist
HINT: sudo modprobe kvm_amd
INFO: Your CPU supports KVM extensions
KVM acceleration can be used
The check still failed. I rebooted. I tried another guide:
$ sudo apt install qemu-kvm bridge-utils virt-manager
But despite my checks, I do not see /dev/kvm
setup
builder@bosgamerz7:~$ egrep -c '(vmx|svm)' /proc/cpuinfo
16
builder@bosgamerz7:~$ kvm-ok
INFO: /dev/kvm does not exist
HINT: sudo modprobe kvm_amd
INFO: For more detailed results, you should run this as root
HINT: sudo /usr/sbin/kvm-ok
I moved to my Ryzen9 box and it was okay
builder@bosgamerz9:~$ kvm-ok
INFO: /dev/kvm exists
KVM acceleration can be used
I had to fix up some FW rules
builder@bosgamerz9:~$ sudo modprobe br_netfilter
builder@bosgamerz9:~$ sudo vi /etc/default/ufw
builder@bosgamerz9:~$ sudo ufw reload
Firewall not enabled (skipping reload)
builder@bosgamerz9:~$ sudo iptables -P FORWARD ACCEPT
But finally it was running
I’ll run interactive for now (though there are steps for using systemd).
builder@bosgamerz9:~$ gitpod runner run
Verifying runner setup...
Starting Linux Runner with ID 01965a14-d1d6-7d90-96e5-bb7453715e99
[INFO ] Initial runner binary path path=/home/builder/gitpod-runner/bin/20250418.301/gitpod-runner
[INFO ] Starting runner process binaryPath=/home/builder/gitpod-runner/bin/20250418.301/gitpod-runner runnerId=01965a14-d1d6-7d90-96e5-bb7453715e99
[INFO ] Starting binary update watcher initialPath=/home/builder/gitpod-runner/bin/20250418.301/gitpod-runner
[INFO ] Started linux runner pid=464370 runnerId=01965a14-d1d6-7d90-96e5-bb7453715e99
Validating system requirements...
ℹ️ About system requirements:
• Gitpod runner needs certain system settings to create secure, isolated environments
• We'll check if your system meets these requirements
• If changes are needed, we'll explain what they are and why they're necessary
• You'll have full control over making these changes
• All changes can be easily reverted if you uninstall the runner later
• For details: https://www.gitpod.io/docs/flex/runners/linux/requirements
Running checks...
│ Checking KVM availability...
✓ KVM validation completed
│ Checking kernel modules...
✓ Kernel modules check completed
│ Checking device files...
✓ Device files check completed
│ Checking network settings...
✓ Network settings check completed
✓ All validations completed successfully
2025/04/21 15:43:30 INFO Redirecting stderr to file=/home/builder/gitpod-runner/state/01965a14-d1d6-7d90-96e5-bb7453715e99/log/2025-04-21T20:43:30Z-zmB.log.stderr
time=2025-04-21T15:43:30.806-05:00 level=INFO msg="Starting Gitpod runner daemon" commit=5d36bfa0bc6b43d410077ffc614b68b1d5d29527 build.time=2025-04-18T05:07:08Z build.version=20250418.301 go.version=go1.24.1 go.os=linux go.arch=amd64 runner.id=01965a14-d1d6-7d90-96e5-bb7453715e99 runner.rootDir=/home/builder/gitpod-runner runner.stateDir=/home/builder/gitpod-runner/state/01965a14-d1d6-7d90-96e5-bb7453715e99 endpoint=https://app.gitpod.io/api
time=2025-04-21T15:43:30.808-05:00 level=INFO msg="Acquired runner lock" pid=464370 lockFile=/home/builder/gitpod-runner/runner.lock
time=2025-04-21T15:43:32.348-05:00 level=INFO msg="got new Nebula host" host=100.64.52.143 hostID=01965a14-d1d6-7d90-96e5-bb7453715e99 serviceHost=false clientDomain=01965a14-d1d6-7d90-96e5-bb7453715e99.us01.gitpod.dev callAgain=1m0s
time=2025-04-21T15:43:32.506-05:00 level=INFO msg=listening addr=:80
time=2025-04-21T15:43:32.731-05:00 level=INFO msg="Connectivity status changed" status=1
time=2025-04-21T15:43:32.731-05:00 level=INFO msg="Runner marked active"
ERRO[0001] DNS resolution failed for static_map host error="lookup lighthouse-i-07cb8e0a65f7dd5e2.us01.gitpod.dev: i/o timeout" hostname=lighthouse-i-07cb8e0a65f7dd5e2.us01.gitpod.dev network=ip4
time=2025-04-21T15:43:32.796-05:00 level=INFO msg="Successfully updated runner configuration schema during startup"
time=2025-04-21T15:43:32.861-05:00 level=INFO msg="IP allocator initialized" subnet=172.26.0.0/16
time=2025-04-21T15:43:32.863-05:00 level=INFO msg="Starting NAT setup"
time=2025-04-21T15:43:32.863-05:00 level=INFO msg="Creating a temporary test table to verify connectivity"
time=2025-04-21T15:43:32.863-05:00 level=INFO msg="Flushing after adding test table"
time=2025-04-21T15:43:32.864-05:00 level=INFO msg="Removing temporary test table"
time=2025-04-21T15:43:32.877-05:00 level=INFO msg="Creating NAT table"
time=2025-04-21T15:43:32.877-05:00 level=INFO msg="Flushing after NAT table creation"
time=2025-04-21T15:43:32.877-05:00 level=INFO msg="Creating NAT chain"
time=2025-04-21T15:43:32.877-05:00 level=INFO msg="Flushing after NAT chain creation"
time=2025-04-21T15:43:32.878-05:00 level=INFO msg="Adding masquerade rule"
time=2025-04-21T15:43:32.878-05:00 level=INFO msg="Performing final flush for NAT setup"
time=2025-04-21T15:43:32.924-05:00 level=INFO msg="NAT setup completed successfully"
time=2025-04-21T15:43:32.924-05:00 level=INFO msg="Forward chain not found; creating" chain=gitpod_runner_forward
time=2025-04-21T15:43:33.361-05:00 level=INFO msg="Running update checker..."
time=2025-04-21T15:43:33.371-05:00 level=INFO msg="Starting periodic update check" interval=1h0m0s
I immediately saw it go online in the web page
I can now pick from a few common Git providers (though, I would have liked to just specify my own HTTP endpoint)
I’ll enable PAT in the pop-up menu
Then continue
Since I’ll be providing runners to developers, they allow me to set the Virtualization settings. Since this host only has 16 cores to start with, I’ll disable the Extra large option
It does appear you can edit the name and description, but notably not the provisioned resources
Lastly, I’ll pick a small environment and a small Python repo for which to create an environment
I provided a clone PAT and it started the process of downloading code…
I now have it running:
However, even after giving it a few hours, it failed to open in VS Code
I tried multiple times and each time it just timed out. I suspect it expects a public IP or having direct internet access
I’m rather stuck. Locally, only “Apple Silicon” is available
Remotely, I can use Linux, albeit so far unsuccessfully and AWS
AWS
I’ll try AWS, but fear what kind of costs i’ll incur
It fires up a CloudFormation template (so I should be able to undo)
But that Cloudformation requires a VPC:
My experience has been VPCs get really expensive real fast because they want IGWs and IGWs, fundamentally, are really small EC2s whose costs add up. It took me a long time to remove all the tentacles of my last VPC and I’m not about to add one back.
GCP
I’ll try firing up a GCP VM. I think an e2-standard-4 with a 100Gb disk should cover it
However, it seems VT-X isn’t there by default
I created the VM with a virtualization option using a gcloud
command
$ cat ./create_vm.sh
#!/bin/bash
gcloud compute instances create instance-20250424-015528 \
--project=myanthosproject2 \
--zone=us-central1-c \
--network-interface=network-tier=PREMIUM,stack-type=IPV4_ONLY,subnet=default \
--metadata=enable-osconfig=TRUE \
--maintenance-policy=MIGRATE \
--provisioning-model=STANDARD \
--service-account=511842454269-compute@developer.gserviceaccount.com \
--scopes=https://www.googleapis.com/auth/devstorage.read_only,https://www.googleapis.com/auth/logging.write,https://www.googleapis.com/auth/monitoring.write,https://www.googleapis.com/auth/service.management.readonly,https://www.googleapis.com/auth/servicecontrol,https://www.googleapis.com/auth/trace.append \
--create-disk=auto-delete=yes,boot=yes,device-name=instance-20250424-015528,image=projects/debian-cloud/global/images/debian-12-bookworm-v20250415,mode=rw,size=10,type=pd-balanced \
--no-shielded-secure-boot \
--shielded-vtpm \
--shielded-integrity-monitoring \
--labels=goog-ops-agent-policy=v2-x86-template-1-4-0,goog-ec-src=vm_add-gcloud \
--reservation-affinity=any \
--enable-nested-virtualization \
--min-cpu-platform="Intel Haswell"
&& \
gcloud compute disks add-resource-policies instance-20250424-015528 \
--project=myanthosproject2 \
--zone=us-central1-c \
--resource-policies=projects/myanthosproject2/regions/us-central1/resourcePolicies/default-schedule-1 \
Then once launched, followed the steps the gitpod instructed including some modprobe commands, firewall rules and adding my user (isaac_johnson) to the KVM group.
$ sudo usermod -aG kvm isaac_johnson
isaac_johnson@instance-20250424-022228:~$
sudo modprobe bridge
sudo modprobe br_netfilter
sudo modprobe nf_tables
sudo modprobe nf_nat
sudo modprobe nf_conntrack
sudo modprobe xt_conntrack
isaac_johnson@instance-20250424-022228:~$
sudo sysctl -w net.ipv4.ip_forward=1
sudo sysctl -w net.bridge.bridge-nf-call-iptables=1
sudo sysctl -w net.bridge.bridge-nf-call-ip6tables=1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
But I did get there
I fired instance up, but it died due to disk space (or lack thereof)
I’ll just add a disk manually
I worked through all the steps to expand add a properly formatted 100Gb volume to /data
isaac_johnson@instance-20250424-015528:~$ df -h
Filesystem Size Used Avail Use% Mounted on
udev 1.8G 0 1.8G 0% /dev
tmpfs 368M 524K 367M 1% /run
/dev/sda1 9.7G 5.3G 3.9G 59% /
tmpfs 1.8G 0 1.8G 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
/dev/sda15 124M 12M 113M 10% /boot/efi
tmpfs 368M 0 368M 0% /run/user/1000
/dev/sdb1 98G 24K 93G 1% /data
Only the gitpod config lacks any option to set path or volume
Configures a self-hosted runner. Two setup methods available:
1. Using an exchange token (--exchange-token) from a runner created in the Gitpod app
2. Creating a new runner directly (requires 'gitpod login')
Usage:
gitpod runner setup [flags]
Flags:
--channel string The release channel to use for the runner, either 'stable' or 'latest'. Only used when creating a new runner. (default "stable")
--exchange-token string Exchange token to use for signing up the runner. Use this to e.g. set up a runner created from the Gitpod app, instead of creating a new runner.
--force Force to setup a new runner. WARNING: This will overwrite the existing runner configuration.
--gateway-endpoint string Gateway endpoint used for secure connectivity to the Runner. Set if you have a custom gateway deployed, otherwise use default. (default "https://us01.gitpod.dev:8443")
-h, --help help for setup
--host string Management plane endpoint used for API requests. Only used when not signed in with 'gitpod login', otherwise the endpoint from the active CLI context is used. (default "https://app.gitpod.io/api")
--name string Name of the runner to create. Ignored if --exchange-token is set and an existing runner is used.
--silent Do not print any progress output
Global Flags:
--config string location of the configuration file (default "~/.gitpod/configuration.yaml")
--context string context to use - defaults to the active context in the configuration file
-v, --verbose display verbose output for more detailed logging
same with run
isaac_johnson@instance-20250424-015528:~$ gitpod runner run --help
Starts the runner
Usage:
gitpod runner run [flags]
Flags:
-h, --help help for run
Global Flags:
--config string location of the configuration file (default "~/.gitpod/configuration.yaml")
--context string context to use - defaults to the active context in the configuration file
-v, --verbose display verbose output for more detailed logging
I’ll try one more time - this time upping the root volume to 100Gb
$ cat ./create_vm.sh
#!/bin/bash
gcloud compute instances create instance-20250424-022228 \
--project=myanthosproject2 \
--zone=us-central1-c \
--network-interface=network-tier=PREMIUM,stack-type=IPV4_ONLY,subnet=default \
--metadata=enable-osconfig=TRUE \
--maintenance-policy=MIGRATE \
--provisioning-model=STANDARD \
--service-account=511842454269-compute@developer.gserviceaccount.com \
--scopes=https://www.googleapis.com/auth/devstorage.read_only,https://www.googleapis.com/auth/logging.write,https://www.googleapis.com/auth/monitoring.write,https://www.googleapis.com/auth/service.management.readonly,https://www.googleapis.com/auth/servicecontrol,https://www.googleapis.com/auth/trace.append \
--create-disk=auto-delete=yes,boot=yes,device-name=instance-20250424-015528,image=projects/debian-cloud/global/images/debian-12-bookworm-v20250415,mode=rw,size=100,type=pd-balanced \
--no-shielded-secure-boot \
--shielded-vtpm \
--shielded-integrity-monitoring \
--labels=goog-ops-agent-policy=v2-x86-template-1-4-0,goog-ec-src=vm_add-gcloud \
--reservation-affinity=any \
--enable-nested-virtualization \
--min-cpu-platform="Intel Haswell"
$ ./create_vm.sh
Created [https://www.googleapis.com/compute/v1/projects/myanthosproject2/zones/us-central1-c/instances/instance-20250424-022228].
WARNING: Some requests generated warnings:
- Disk size: '100 GB' is larger than image size: '10 GB'. You might need to resize the root repartition manually if the operating system does not support automatic resizing. See https://cloud.google.com/compute/docs/disks/add-persistent-disk#resize_pd for details.
NAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS
instance-20250424-022228 us-central1-c n1-standard-1 10.128.0.52 34.30.146.247 RUNNING
This time it stayed up
I’ll create a new environment
I see an error
and in the logs on the host
time=2025-04-24T02:48:53.697Z level=ERROR msg="Cannot start environment - this is a system exception" environment=019665b2-82b6-7219-92c4-8bd6fc27c249 err="failed to create environment: failed to create config disk: error creating disk image: error creating filesystem: exec: \"mkfs\": executable file not found in $PATH\n"
time=2025-04-24T02:49:07.103Z level=WARN msg="environment not found" id=019665b2-82b6-7219-92c4-8bd6fc27c249
I see that mkfs is in /sbin which is not in the PATH by default. I’ll add it (but worry I’ll need to move to sudo later)
isaac_johnson@instance-20250424-022228:~$ which mkfs
isaac_johnson@instance-20250424-022228:~$ sudo which mkfs
/usr/sbin/mkfs
isaac_johnson@instance-20250424-022228:~$ export PATH=$PATH:/usr/sbin
isaac_johnson@instance-20250424-022228:~$ which mkfs
/usr/sbin/mkfs
This seemed to move ahead
After a bit it showed it was running
This time launching in VS Code did work
I noticed it created a .devcontainer for me. This is not in my repo
In the terminal, I’m a bit thrown by the 178Gb free space. My local WSL just has 43Gb free and this remote host had a single 100Gb drive.
I did see it really does use up the CPU running KVM
And was reasonable on memory
SSH
If I wanted to SSH directly, I have to use the Github env setup
In WSL, I can add the Gitpod CLI, then setup SSH keys
builder@DESKTOP-QADGF36:/mnt/c/Users/isaac/Downloads$ curl -o gitpod -fsSL "https://releases.gitpod.io/cli/stable/gitpod-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/amd64/;s/\(arm64\|aarch64\)/arm64/')" && \
/bin> chmod +x gitpod && \
> sudo mv gitpod /usr/local/bin
[sudo] password for builder:
builder@DESKTOP-QADGF36:/mnt/c/Users/isaac/Downloads$ gitpod env ssh-config
Error: no active context
Possible resolutions:
- sign in using `gitpod login`
- select an existing context using `gitpod config context use <context>`
builder@DESKTOP-QADGF36:/mnt/c/Users/isaac/Downloads$ gitpod login
[ERROR] could not open browser, please open the following URL manually
https://app.gitpod.io/auth/oauth2/authorize?client_id=gitpod_cli&code_challenge=1k5gfGm35pQ24mrr4vBlzXkZwZlT6Uc57uVUJPY_K0o&code_challenge_method=S256&redirect_uri=http%3A%2F%2Flocalhost%3A63110&response_type=code&state=026797b8e4c9638873ae0a7cfb8c4e926eb2ecf9fc5b2edfc565a850263a43c4
[WARN ] login successful host=https://app.gitpod.io
[INFO ] login successful
User name: Isaac Johnson
User ID: 01965a03-6623-7f39-8eef-5eaa86505d71
Organization: Freshbrewed
Organization ID: 01965a03-660c-79f9-a53a-d0d172ffc3f0
Host: https://app.gitpod.io
builder@DESKTOP-QADGF36:/mnt/c/Users/isaac/Downloads$ gitpod env ssh-config
[WARN ] no SSH keys found, generating a new one
[INFO ] generated SSH key pair privateKey=/home/builder/.ssh/gitpod/id_ed25519 publicKey=/home/builder/.ssh/gitpod/id_ed25519.pub
But SSH just times out
builder@DESKTOP-QADGF36:/mnt/c/Users/isaac/Downloads$ ssh 019665b2-82b6-7219-92c4-8bd6fc27c249.gitpod.environment
I wanted to use my Ryzen9 host, but it can’t fire a browser
and the authorization doesn’t do stuff behind the scenes, rather assumes it can talk back on localhost
I used wget 'url'
in another SSH session to the Ryzen host to get it past this hurdle.
But yet again, we get a timeout
I tried gitpod ssh as well
builder@bosgamerz9:~$ gitpod environment ssh 019665b2-82b6-7219-92c4-8bd6fc27c249.gitpod.environment
Error: cannot wait for key to become ready: context deadline exceeded
[ERROR] cannot watch events error="cannot watch events: deadline_exceeded: context deadline exceeded"
I’m going to stop at this point as I’ve done more than a fair amount to get SSH to work.
They have some more suggestions in the docs.
Dead Environments
If you had an environment but the host had issues or was removed, you are rather stuck.
For instance, we can see I have some uncommitted changes, but I’m stuck as that host is now gone
Cleanup
We can delete our environment from the “…” menu
It will warn me about deleting all content
When cleaning up VMs, don’t forget to also delete disks
Pricing and next steps
I’m always a bit hesitant when they won’t show pricing
More that that, you have to give a work email to just see the features in “Core”
I did see some prices listed in TrustRadius that suggest it has been around $30/mo/user
But I also saw a blog post from 2024 that shows they just changed models this year.
Summary
Today we set up Gitpod first on some local Linux computers and then a cloud instance. We were successful using the GCP virtual machine - once we figured out how to set virtualization properly, add enough disk space, and add the user to use KVM. The consequence of this, however, is it means paying for a persistent larger cloud-based VM with enough disc and RAM to run Gitpod locally.
With regards to local development, currently, the Gitpod local version is limited to just the newer versions of MacBook laptops. That may cover a lot of software development shops, but not all and in many cases I see software developers having a mix of Windows and Mac laptops. Having a solution that doesn’t natively support Windows really limits Gitpod’s usability.
One thing that we need to remember that Gitpod solves differently than competitors like Github Codespaces or Coder is that this is creating a KVM machine for you that has CPU memory and disk. This allows a developer who needs to have local resources like a local Valkey/Redis or a local Database to do development. Conversely, a consequence of tools like Code-server and Codespaces is that because they are running in a small container one cannot really also run a big database. One might be able to pull off a sqlite instance, but it would be hard to run and locally test an app that needs some heavier infrastructure or bigger specs in just a code-server container. The other point to bring up is that Gitpod has been around for over 6 years. KVM six years ago was still very cutting edge.
That said, I do have concerns on the pricing model. It seems like up until 2024 they had clear exposed and documented pricing under their “Classic” plan but now in 2025, for whatever reason, they are hiding the prices and making it a negotiated deal. As a user who would consider a solution like this, price obfuscation really gives me pause. I do think that I will come back and see how Gitpod is doing in a few months and see whether they have an easier to support OpenTofu for Azure or GCP, or perhaps a client that would work natively under Windows.