Published: Apr 14, 2025 by Isaac Johnson
At GCP Next25 I gravitated towards a smaller booth for Coder. The logo looked familiar but I couldn’t readily place it untill I realized this is the same Coder app I’ve been using for years in my cluster.
Like all vendors and booths, they had some AI features in the pipe and some recent updates worth checking out.
Today, let’s first tackle updating our Coder from v4.98.2, which is still pretty current, to the latest 4.99 release.
Then we can explore new features
Updating
The reason my codeserver was relatively recent is I pull from latest:
$ kubectl get deployment code-server-deployment -o yaml | grep image | tail -n2
image: lscr.io/linuxserver/code-server:latest
imagePullPolicy: Always
And just recently we can see the pod was rotated:
$ kubectl get deployment code-server-deployment -o yaml | tail -n 19
status:
availableReplicas: 1
conditions:
- lastTransitionTime: "2024-03-02T14:33:12Z"
lastUpdateTime: "2024-05-18T16:26:27Z"
message: ReplicaSet "code-server-deployment-69fb568f4b" has successfully progressed.
reason: NewReplicaSetAvailable
status: "True"
type: Progressing
- lastTransitionTime: "2025-04-02T22:56:55Z"
lastUpdateTime: "2025-04-02T22:56:55Z"
message: Deployment has minimum availability.
reason: MinimumReplicasAvailable
status: "True"
type: Available
observedGeneration: 2
readyReplicas: 1
replicas: 1
updatedReplicas: 1
I’m willing to bet that if I look at my current version:
Then rotate the pod
$ kubectl delete po code-server-deployment-69fb568f4b-h5mhq
pod "code-server-deployment-69fb568f4b-h5mhq" deleted
$ kubectl get po -A | grep code-server
default code-server-deployment-69fb568f4b-7q7bd 0/1 ContainerCreating 0 14s
I can see the latest version
Cloud Code (GCP)
Let’s look first at what we can do with the “Cloud Code” plugin.
We first need to auth to GCP
I then have access to a variety of offerings in GCP
For instance, I could create a GCP SM secret
Once created, I can see some details on how to access
For which I can easily verify in the cloud console
Gemini Code Assist
We can also access Gemini Code Assist from the Gemini menu as well
Here, let’s have it create a python app that can show the secret we created:
We can see it assumed a few things (like the session had the gcloud binary) but it was something easily to address.
I could now see a python script that could fetch the value:
We can also see Gemini Code Assist gave us three ways to auth:
If I were to build an app to run in my K3s or Docker, i would likely use method 2 and create an SA with JSON to use.
Continue Dev
We have yet another way to engage with Gemini - via the “Continue.dev” plugin.
I’ll start by going to the GCP AI Studio and using or creating a new API Key
I can now apply that in the Config to add, for instance, Gemini 2.0 Flash
Which works well
However, if I try and use Gemini Pro. I’ll get an error about “models/gemini-pro is not found for API version v1beta”
However, if I go to models, I can pick from another model available at my tier
As you can see, that worked
I do not see cost immediately reflected, but I would expect something later, even if just a few cents
Pushing Code
At this point we have a file but it’s entirely local to this containerized code-server
Let’s create a new repo in Github
Once, created, we can follow the steps to push our code
Here we can see how we can push up master (or main)
Coder
We reviewed “code-server” above, which is excellent and I use it often.
However, Coder (the company) also has a “Coder” app and server different from “Code-server”.
Let’s follow this guide to setting it up locally
I’ll first create a namespace then install an off-the-shelf PostgreSQL for Code to use:
$ kubectl create namespace coder
namespace/coder created
$ helm repo add bitnami https://charts.bitnami.com/bitnami
"bitnami" already exists with the same configuration, skipping
$ helm install coder-db bitnami/postgresql --namespace coder --set auth.username=coder --set auth.password=coder --set auth.database=coder --set persistence.size=10Gi
NAME: coder-db
LAST DEPLOYED: Mon Apr 14 14:03:29 2025
NAMESPACE: coder
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
CHART NAME: postgresql
CHART VERSION: 16.4.5
APP VERSION: 17.2.0
Did you know there are enterprise versions of the Bitnami catalog? For enhanced secure software supply chain features, unlimited pulls from Docker, LTS support, or application customization, see Bitnami Premium or Tanzu Application Catalog. See https://www.arrow.com/globalecs/na/vendors/bitnami for more information.
** Please be patient while the chart is being deployed **
PostgreSQL can be accessed via port 5432 on the following DNS names from within your cluster:
coder-db-postgresql.coder.svc.cluster.local - Read/Write connection
To get the password for "postgres" run:
export POSTGRES_ADMIN_PASSWORD=$(kubectl get secret --namespace coder coder-db-postgresql -o jsonpath="{.data.postgres-password}" | base64 -d)
To get the password for "coder" run:
export POSTGRES_PASSWORD=$(kubectl get secret --namespace coder coder-db-postgresql -o jsonpath="{.data.password}" | base64 -d)
To connect to your database run the following command:
kubectl run coder-db-postgresql-client --rm --tty -i --restart='Never' --namespace coder --image docker.io/bitnami/postgresql:17.2.0-debian-12-r8 --env="PGPASSWORD=$POSTGRES_PASSWORD" \
--command -- psql --host coder-db-postgresql -U coder -d coder -p 5432
> NOTE: If you access the container using bash, make sure that you execute "/opt/bitnami/scripts/postgresql/entrypoint.sh /bin/bash" in order to avoid the error "psql: local user with ID 1001} does not exist"
To connect to your database from outside the cluster execute the following commands:
kubectl port-forward --namespace coder svc/coder-db-postgresql 5432:5432 &
PGPASSWORD="$POSTGRES_PASSWORD" psql --host 127.0.0.1 -U coder -d coder -p 5432
WARNING: The configured password will be ignored on new installation in case when previous PostgreSQL release was deleted through the helm command. In that case, old PVC will have an old password, and setting it through helm won't take effect. Deleting persistent volumes (PVs) will solve the issue.
WARNING: There are "resources" sections in the chart not set. Using "resourcesPreset" is not recommended for production. For production installations, please set the following values according to your workload needs:
- primary.resources
- readReplicas.resources
+info https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
I then create a secret that the Coder helm chart will consume for Database credentials
$ kubectl create secret generic coder-db-url -n coder --from-literal=url="postgres://coder:coder@coder-db-postgresql.coder.svc.cluster.local:5432/coder?sslmode=disable"
secret/coder-db-url created
I’ll create a quick values file that has this secret listed:
$ cat values.yaml
coder:
# You can specify any environment variables you'd like to pass to Coder
# here. Coder consumes environment variables listed in
# `coder server --help`, and these environment variables are also passed
# to the workspace provisioner (so you can consume them in your Terraform
# templates for auth keys etc.).
#
# Please keep in mind that you should not set `CODER_HTTP_ADDRESS`,
# `CODER_TLS_ENABLE`, `CODER_TLS_CERT_FILE` or `CODER_TLS_KEY_FILE` as
# they are already set by the Helm chart and will cause conflicts.
env:
- name: CODER_PG_CONNECTION_URL
valueFrom:
secretKeyRef:
# You'll need to create a secret called coder-db-url with your
# Postgres connection URL like:
# postgres://coder:password@postgres:5432/coder?sslmode=disable
name: coder-db-url
key: url
# For production deployments, we recommend configuring your own GitHub
# OAuth2 provider and disabling the default one.
- name: CODER_OAUTH2_GITHUB_DEFAULT_PROVIDER_ENABLE
value: "false"
# (Optional) For production deployments the access URL should be set.
# If you're just trying Coder, access the dashboard via the service IP.
# - name: CODER_ACCESS_URL
# value: "https://coder.example.com"
#tls:
# secretNames:
# - my-tls-secret-name
Then I can install the latest
$ helm install coder coder-v2/coder --namespace coder --values values.yaml --version 2.21.0
NAME: coder
LAST DEPLOYED: Mon Apr 14 14:06:11 2025
NAMESPACE: coder
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Enjoy Coder! Please create an issue at https://github.com/coder/coder if you run
into any problems! :)
Next, I watch for the pods to come up
$ kubectl get po -n coder
NAME READY STATUS RESTARTS AGE
coder-db-postgresql-0 1/1 Running 0 3m
coder-7984578b8b-tvzgr 0/1 ContainerCreating 0 18s
$ kubectl get po -n coder
NAME READY STATUS RESTARTS AGE
coder-db-postgresql-0 1/1 Running 0 3m28s
coder-7984578b8b-tvzgr 1/1 Running 0 46s
I can now port-forward to the service
$ kubectl get svc -n coder
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
coder-db-postgresql-hl ClusterIP None <none> 5432/TCP 3m42s
coder-db-postgresql ClusterIP 10.43.96.242 <none> 5432/TCP 3m42s
coder LoadBalancer 10.43.241.249 <pending> 80:32239/TCP 60s
$ kubectl port-forward -n coder svc/coder 8088:80
Forwarding from 127.0.0.1:8088 -> 8080
Forwarding from [::1]:8088 -> 8080
I’m presented with a setup
I’m now in a workspace dashboard
Before I can create a workspace, I need to create a template
I started with a Docker template
But it failed to connect to the docker daemon (likely because Coder wasn’t launched in a priveledged mode)
Let’s try Kubernetes instead. Set the namespace
Then we can deploy. What threw me off is the output is terraform/opentofu
Is that meant to explain what it is making? Or something I’m expected to execute?
I’ll try and use it with a new workspace
Which shows it’s using TF 1.11
In short order I see it running
If I click “code-server” it pops up a fresh coder instance
If I do “VS Code Desktop”
I see it try and use SSH - I doubt this will work because I’m only port-forwarding on HTTP
Lastly, I can just kick open a Terminal as well if command line is what I’m after
Timeline
I thought it was interesting we can see a timeline for how long it took to build this Workspace.
I’m not sure what I would do with that information, but there it is.
Stopping
I can also just stop a Coder workspace. This is feeling very much like Github Codespaces
Once I stop it
I can always open it to start it again
Ingress
Let’s add an Azure DNS record
$ az account set --subscription "Pay-As-You-Go" && az network dns record-set a add-record -g idjdnsrg -z tpk.pw -a 75.73.224.240 -n coder
{
"ARecords": [
{
"ipv4Address": "75.73.224.240"
}
],
"TTL": 3600,
"etag": "8a170119-c778-473a-af11-ce2df2cfb15c",
"fqdn": "coder.tpk.pw.",
"id": "/subscriptions/d955c0ba-13dc-44cf-a29a-8fed74cbb22d/resourceGroups/idjdnsrg/providers/Microsoft.Network/dnszones/tpk.pw/A/coder",
"name": "coder",
"provisioningState": "Succeeded",
"resourceGroup": "idjdnsrg",
"targetResource": {},
"trafficManagementProfile": {},
"type": "Microsoft.Network/dnszones/A"
}
In the values I’m going to set the Ingress details
ingress:
annotations:
cert-manager.io/cluster-issuer: azuredns-tpkpw
apiVersion: ""
enabled: true
extraHosts: []
extraPaths: []
extraRules: []
extraTls: []
hostname: coder.tpk.pw
ingressClassName: nginx
path: /
pathType: ImplementationSpecific
secrets:
- codercert
selfSigned: false
tls: true
and change the service to a ClusterIP
service:
annotations: {}
enable: true
externalTrafficPolicy: Cluster
httpNodePort: ""
httpsNodePort: ""
loadBalancerClass: ""
loadBalancerIP: ""
sessionAffinity: None
type: ClusterIP
I’ll now try an upgrade to see if it picks it up
$ helm upgrade coder coder-v2/coder --namespace coder --values coder.values.yaml --version 2.21.0
coalesce.go:220: warning: cannot overwrite table with non table for coder.coder.ingress.tls (map[enable:false secretName: wildcardSecretName:])
coalesce.go:220: warning: cannot overwrite table with non table for coder.coder.ingress.tls (map[enable:false secretName: wildcardSecretName:])
Release "coder" has been upgraded. Happy Helming!
NAME: coder
LAST DEPLOYED: Mon Apr 14 15:08:48 2025
NAMESPACE: coder
STATUS: deployed
REVISION: 2
TEST SUITE: None
NOTES:
Enjoy Coder! Please create an issue at https://github.com/coder/coder if you run
into any problems! :)
I see it did switch the service, but did not address the ingress
$ kubectl get svc -n coder
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
coder-db-postgresql-hl ClusterIP None <none> 5432/TCP 65m
coder-db-postgresql ClusterIP 10.43.96.242 <none> 5432/TCP 65m
coder ClusterIP 10.43.241.249 <none> 80/TCP 63m
$ kubectl get ingress -n coder
No resources found in coder namespace.
No bother, I can just create it myself
$ cat coder.ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: azuredns-tpkpw
ingress.kubernetes.io/ssl-redirect: "true"
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.org/websocket-services: coder
name: coderingress
spec:
rules:
- host: coder.tpk.pw
http:
paths:
- backend:
service:
name: coder
port:
number: 80
path: /
pathType: ImplementationSpecific
tls:
- hosts:
- coder.tpk.pw
secretName: coder-tls
$ kubectl apply -f ./coder.ingress.yaml -n coder
ingress.networking.k8s.io/coderingress created
When I see the cert is satisfied
$ kubectl get cert -n coder
NAME READY SECRET AGE
coder-tls False coder-tls 27s
$ kubectl get cert -n coder
NAME READY SECRET AGE
coder-tls True coder-tls 81s
I can now login from the proper HTTPS URL
Which makes it handy to get at resources in my network
But because I’m using Nginx, not a proper cloud LB or Istio, I know SSH will not work
However - IF I was in an environment where SSH would work, I could use the Coder VS Code plugin to connect to them
Pricing
Coder does have a premium option. Consider it “Github Codespaces” without the Github. I could very much see this valuable for On-Prem organizations.
However, there is no actual pricing listed
I could definately see value in having logging and workspace cleanup controls, among many other premium features
Summary
Coder is a great option for those that would want more than just the one hosted code instance that code-server would give. It actually has quite a few templates and covers the major Cloud providers including AWS, Azure, DigitalOcean and GCP
However, one of my gripes here is it doesn’t let me specify credentials - either by way of settings in a provider or environment keys.
For instance, if we look at the Azure VM provider we can see it just assumes we are logged in to an Azure account
That seems like a rather big limitation (as I would very likely have Coder running in a Production subscription and want my workspaces set in a Development subscription).
That said, I’m overall impressed with the offering (especially all the features in the pure OS version). It is something definately worth checking out.