{"id":23564739,"url":"https://github.com/mickaelbaron/coder-k3s-guide","last_synced_at":"2026-04-13T22:33:57.035Z","repository":{"id":188443577,"uuid":"609260858","full_name":"mickaelbaron/coder-k3s-guide","owner":"mickaelbaron","description":"Guide for installing Coder on a k3s cluster and other cool stuff ","archived":false,"fork":false,"pushed_at":"2023-03-17T08:54:13.000Z","size":189,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-16T04:36:19.670Z","etag":null,"topics":["apache2","coder","docker","guide","k3s","kubernetes","nginx","reverse-proxy"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mickaelbaron.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"MIT-LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-03-03T18:03:17.000Z","updated_at":"2025-03-11T14:27:40.000Z","dependencies_parsed_at":"2023-08-15T11:24:18.738Z","dependency_job_id":null,"html_url":"https://github.com/mickaelbaron/coder-k3s-guide","commit_stats":null,"previous_names":["mickaelbaron/coder-k3s-guide"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mickaelbaron/coder-k3s-guide","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mickaelbaron%2Fcoder-k3s-guide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mickaelbaron%2Fcoder-k3s-guide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mickaelbaron%2Fcoder-k3s-guide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mickaelbaron%2Fcoder-k3s-guide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mickaelbaron","download_url":"https://codeload.github.com/mickaelbaron/coder-k3s-guide/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mickaelbaron%2Fcoder-k3s-guide/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31774028,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-13T20:17:16.280Z","status":"ssl_error","status_checked_at":"2026-04-13T20:17:08.216Z","response_time":93,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apache2","coder","docker","guide","k3s","kubernetes","nginx","reverse-proxy"],"created_at":"2024-12-26T17:17:28.277Z","updated_at":"2026-04-13T22:33:57.008Z","avatar_url":"https://github.com/mickaelbaron.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Running [Coder](https://coder.com/) in [K3s](https://k3s.io/)\n\n[Coder](https://coder.com/) is a self-Hosted Remote Development Platform which allows to build your code on servers. This guide helps to deploy [Coder](https://coder.com/) on a k3s cluster.\n\nBefore starting the [Coder](https://coder.com/) install process, you must have:\n\n* The following infrastructure (to reproduce the experimentation):\n  * Three Ubuntu 22.04 machines with SSH credentials.\n    * The hostname of each machine (physical or virtual) is: `k3sserver`, `k3snode1` and `k3snode2`.\n    * All machines are on the same vlan.\n    * All machines have ports 22 (SSH), 80 (HTTP), 443 (HTTPS) and 6443 (Kubernetes) exposed.\n  * A domain (coder.mydomain.com), a subdomain (*.coder.mydomain.com) and a configured DNS to redirect to `k3sserver`.\n  * A reverse Proxy (Apache HTTP or NGINX) which will be hosted outside of the Kubernetes cluster.\n  * A [Docker](https://www.docker.com/) installation on `k3sserver` to deploy the Reverse Proxy.\n\n* Locally\n  * [kubectl](https://kubernetes.io/docs/reference/kubectl/)\n  * [HELM](https://helm.sh/)\n\nThe plan of this guide is the following:\n\n* [Install k3s](#install-k3s)\n* [Deploy Coder](#deploy-coder)\n* [Deploy Reverse Proxy](#deploy-reverse-proxy)\n    * [NGINX](#nginx)\n    * [Apache HTTP](#apache-http)\n* [Run](#run)\n\n## Install k3s\n\n* Connect to the server node (`k3sserver`) and run:\n\n```console\n$ curl -sfL https://get.k3s.io | sh -\n```\n\n* Extract the [K3s](https://k3s.io/) token on the server node:\n\n```console\n$ sudo cat /var/lib/rancher/k3s/server/node-token\nK20545dbddda0f19bf1c9ac794546d200cdc4ede3fe9ad82d5e560ad0748cc28fd4::server:17a174d18d4fd82c0f99b687bd9aabcd\n```\n\n\u003e 🤓 This is my own [K3s](https://k3s.io/) token. You will have to adapt the next few instructions with YOUR [K3s](https://k3s.io/) token.\n\n* Connect to the first agent node (`k3snode1`) and run:\n\n```console\n$ export K3S_TOKEN=K20545dbddda0f19bf1c9ac794546d200cdc4ede3fe9ad82d5e560ad0748cc28fd4::server:17a174d18d4fd82c0f99b687bd9aabcd\n$ curl -sfL https://get.k3s.io | K3S_URL=https://k3sserver:6443 sh -\n```\n\n* Connect to the second agent node (`k3snode2`) and execute the same command line:\n\n```console\n$ export K3S_TOKEN=K20545dbddda0f19bf1c9ac794546d200cdc4ede3fe9ad82d5e560ad0748cc28fd4::server:17a174d18d4fd82c0f99b687bd9aabcd\n$ curl -sfL https://get.k3s.io | K3S_URL=https://k3sserver:6443 sh -\n```\n\n* To get the cluster access file (_k3s.yaml_), from your host, run:\n\n```console\n$ scp k3sserver:/etc/rancher/k3s/k3s.yaml .\n```\n\n* Update the [K3s](https://k3s.io/) server address (old value: 127.0.0.1) with the new hostname:\n\n```console\n$ sed -i '' \"s/127.0.0.1/k3sserver/\" k3s.yaml\n```\n\n* Check the [K3s](https://k3s.io/) cluster:\n\n```console\n$ export KUBECONFIG=$PWD/k3s.yaml\n$ kubectl get nodes\nNAME        STATUS   ROLES                  AGE   VERSION\nk3sserver   Ready    control-plane,master   21d   v1.25.6+k3s1\nk3snode1    Ready    \u003cnone\u003e                 21d   v1.25.6+k3s1\nk3snode2    Ready    \u003cnone\u003e                 21d   v1.25.6+k3s1\n```\n\n\u003e 📄 These instructions are based on the [K3s](https://k3s.io/) website: https://docs.k3s.io/quick-start.\n\n## Deploy Coder\n\n* Create a namespace for [Coder](https://coder.com/), named `coder` in this example:\n\n```console\n$ kubectl create namespace coder\nnamespace/coder created\n```\n\n* Deploy PostgreSQL on the [K3s](https://k3s.io/) cluster from the [Bitnami](https://bitnami.com/) repository:\n\n```console\n$ helm repo add bitnami https://charts.bitnami.com/bitnami\n\"bitnami\" has been added to your repositories\n\n$ helm install coder-db bitnami/postgresql \\\n    --namespace coder \\\n    --set auth.username=coder \\\n    --set auth.password=coder \\\n    --set auth.database=coder \\\n    --set persistence.size=10Gi\n\nNAME: coder-db\nLAST DEPLOYED: Wed Feb  31 09:53:58 2666\nNAMESPACE: coder\nSTATUS: deployed\nREVISION: 1\nTEST SUITE: None\nNOTES:\nCHART NAME: postgresql\nCHART VERSION: 11.1.28\nAPP VERSION: 14.2.0\n\n** Please be patient while the chart is being deployed **\n\nPostgreSQL can be accessed via port 5432 on the following DNS names from within your cluster:\n\n    coder-db-postgresql.coder.svc.cluster.local - Read/Write connection\n\nTo get the password for \"postgres\" run:\n\n    export POSTGRES_ADMIN_PASSWORD=$(kubectl get secret --namespace coder coder-db-postgresql -o jsonpath=\"{.data.postgres-password}\" | base64 --decode)\n\nTo get the password for \"coder\" run:\n\n    export POSTGRES_PASSWORD=$(kubectl get secret --namespace coder coder-db-postgresql -o jsonpath=\"{.data.password}\" | base64 --decode)\n\nTo connect to your database run the following command:\n\n    kubectl run coder-db-postgresql-client --rm --tty -i --restart='Never' --namespace coder --image docker.io/bitnami/postgresql:14.2.0-debian-10-r88 --env=\"PGPASSWORD=$POSTGRES_PASSWORD\" \\\n      --command -- psql --host coder-db-postgresql -U coder -d coder -p 5432\n\n    \u003e NOTE: If you access the container using bash, make sure that you execute \"/opt/bitnami/scripts/entrypoint.sh /bin/bash\" in order to avoid the error \"psql: local user with ID 1001} does not exist\"\n\nTo connect to your database from outside the cluster execute the following commands:\n\n    kubectl port-forward --namespace coder svc/coder-db-postgresql 5432:5432 \u0026\n    PGPASSWORD=\"$POSTGRES_PASSWORD\" psql --host 127.0.0.1 -U coder -d coder -p 5432\n```\n\n* Verify that a PostgreSQL pod has been created:\n\n```console\n$ kubectl get pods --namespace coder\nNAME                     READY   STATUS    RESTARTS   AGE\ncoder-db-postgresql-0    1/1     Running   0          22d\n```\n\nThe cluster-internal DB URL for the PostgreSQL database is:\n\n```\npostgres://coder:coder@coder-db-postgresql.coder.svc.cluster.local:5432/coder?sslmode=disable\n```\n\n* Create a secret with the database URL:\n\n```console\n$ 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\"\n```\n\n* Add the Coder Helm repository:\n\n```console\n$ helm repo add coder-v2 https://helm.coder.com/v2\n```\n\n* Create a [values.yaml](coder/values.yaml) configuration file with the suitable settings for your deployment. You should at least update the content following the `# TODO` comments:\n\n```yaml\ncoder:\n  env:\n    - name: CODER_PG_CONNECTION_URL\n      valueFrom:\n        secretKeyRef:\n          name: coder-db-url\n          key: url\n\n    - name: CODER_ACCESS_URL\n      # TODO\n      value: \"https://coder.mydomain.com\"\n    - name: CODER_WILDCARD_ACCESS_URL\n      # TODO\n      value: \"*.coder.mydomain.com\"\n\n  service:\n    enable: true\n    type: ClusterIP\n    sessionAffinity: ClientIP\n    externalTrafficPolicy: Cluster\n    loadBalancerIP: \"\"\n    annotations: {}\n\n  ingress:\n    enable: true\n    className: \"\"\n    # TODO\n    host: \"coder.mydomain.com\"\n    # TODO\n    wildcardHost: \"*.coder.mydomain.com\"\n    annotations: {}\n    tls:\n      enable: false\n      secretNames: \"\"\n      wildcardSecretName: \"\"\n```\n\nThe service will be configured as a ClusterIP. We configure Ingress to handle requests for the (`coder.mydomain.com`) domain.\n\n* Install the HELM chart on your [K3s](https://k3s.io/) cluster:\n\n```console\n$ helm install coder coder-v2/coder --namespace coder --values values.yaml\n...\n```\n\n* Check the pods into the `coder` namespace:\n\n```console\n$ kubectl get pods --namespace coder\nNAME                     READY   STATUS    RESTARTS   AGE\ncoder-db-postgresql-0    1/1     Running   0          22d\ncoder-59c6bc9c77-6f2wj   1/1     Running   0          9m47s\n```\n\n\u003e 📄 These instructions are based on the [Coder](https://coder.com/) website: https://coder.com/docs/v2/latest/install/kubernetes.\n\n## Deploy Reverse Proxy\n\nAs mentioned in the introduction, a Reverse Proxy will be deployed outside of your Kubernetes cluster.\n\nThe Reverse Proxy will also be in charge of managing SSL/TLS certificates. Let's describe how to generate certificates with LetsEncrypt.\n\n* Connect to the server node (`k3sserver`) and install Certbot:\n\n```console\n$ sudo apt-get update \n$ sudo apt-get install certbot -y\n```\n\n* Create the SSL/TLS certificates:\n\n```console\n$ sudo certbot certonly --agree-tos -m YOUR_EMAIL --manual --preferred-challenges=dns -d 'coder.mydomain.com' -d '*.coder.mydomain.com' -v\n...\n```\n\n* Copy the SSL/TLS certificates files (_fullchain.pem_ and _privkey.pem_) into a directory (i.e. _/ssl_):\n\n```console\n$ mkdir /ssl\n$ cp /etc/letsencrypt/live/coder.mydomain.com-0001/fullchain.pem privkey.pem /ssl\n```\n\n* Generate _dhparams.pem_ file:\n\n```console\n$ cd /ssl\n$ openssl dhparam -out dhparams.pem 4096\n```\n\nYou need to configure your Kubernetes cluster to update HTTP and HTTPS listen ports.\n\n* Connect to the server node (`k3sserver`) and create a _/var/lib/rancher/k3s/server/manifests/traefik-config.yaml_ file with the following content:\n\n```yaml\nkind: HelmChartConfig\nmetadata:\n  name: traefik\n  namespace: kube-system\nspec:\n  valuesContent: |-\n    ports:\n      web:\n        exposedPort: 8080\n      websecure:\n        exposedPort: 8443\n```\n\n* Apply this configuration:\n\n```console\n$ kubectl apply -f /var/lib/rancher/k3s/server/manifests/traefik-config.yaml\n```\n\nWe suppose [Docker](https://www.docker.com/) is installed on the server node (`k3sserver`). \n\n* Create a Docker network called `reverseproxynetwork`:\n\n```\n$ docker network create reverseproxynetwork\n```\n\nTwo Reverse Proxy solutions will be presented: [NGINX](https://www.nginx.com/) and [Apache HTTP](https://httpd.apache.org/). Choose only ONE at your convenience.\n\n### NGINX\n\n* Connect to the server node (`k3sserver`).\n\n* Create an _nginx_ directory:\n\n```console\n$ mkdir ~/nginx\n``` \n\n* Create an NGINX configuration file [~/nginx/conf/coder.conf](nginx/conf/coder.conf) with the following content:\n\n```\nserver {\n    listen       80;\n    listen       [::]:80;\n    server_name  *.coder.mydomain.com;\n    return\t 301 https://$host$request_uri;\n}\n\nserver {\n   listen       443 ssl;\n   server_name  *.coder.mydomain.com;\n\n   ssl_protocols TLSv1.2 TLSv1.3;\n   ssl_certificate /ssl/fullchain.pem;\n   ssl_certificate_key /ssl/privkey.pem;\n   ssl_dhparam /ssl/dhparam.pem;\n   ssl_ecdh_curve secp384r1;\n   ssl_prefer_server_ciphers on;\n   ssl_ciphers EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH;\n\n   location / {\n       proxy_pass http://k3sserver:8080/;\n       proxy_http_version 1.1;\n       proxy_set_header Upgrade $http_upgrade;\n       proxy_set_header Connection \"Upgrade\";\n       proxy_set_header Host $host;\n   }\n}\n```\n\n* Create a file [~/nginx/docker-compose.yaml](nginx/docker-compose.yaml) with the following content:\n\n```yaml\nservices:\n\n  nginx:\n    container_name: nginx\n    image: nginx:latest\n    volumes:\n      - ./conf:/etc/nginx/conf.d\n      - /ssl:/ssl\n    restart: always\n    ports:\n      - \"80:80\"\n      - \"443:443\"\n    networks:\n      - reverseproxynetwork\n\nnetworks:\n  reverseproxynetwork:\n    name: reverseproxynetwork\n    external: true\n```\n\n* Create and start the NGINX container:\n\n```console\n$ cd ~/nginx\n$ docker compose up -d\n```\n\n### Apache HTTP\n\n* Connect to the server node (`k3sserver`).\n\n* Create an _apachehttp_ directory.\n\n```console\n$ mkdir ~/apachehttp\n``` \n\n* Create an Apache HTTP configuration file [~/apachehttp/conf/coder.conf](apachehttp/conf/coder.conf) with the following content:\n\n```\n\u003cVirtualHost *:443\u003e\n    SSLEngine On\n    SSLProxyEngine on\n\n    SSLProxyVerify none\n    SSLProxyCheckPeerCN off\n    SSLProxyCheckPeerName off\n    SSLProxyCheckPeerExpire off\n\n    SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1\n    SSLHonorCipherOrder On\n    SSLCipherSuite \"EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4 !SHA1 !SHA256 !SHA384\"\n    SSLCompression off\n\n    # HSTS (http://fr.wikipedia.org/wiki/HTTP_Strict_Transport_Security)\n    Header unset Strict-Transport-Security\n    Header always set Strict-Transport-Security \"max-age=31536000; includeSubDomains; preload\"\n    RequestHeader set X-Forwarded-Proto \"https\"\n    RequestHeader set X-Forwarded-Port \"443\"\n\n    # Certificates\n    SSLCertificateFile /ssl/fullchain.pem\n    SSLCertificateKeyFile /ssl/privkey.pem\n\n    ServerName *.coder.mydomain.com\n\n    ProxyPreserveHost On\n    ProxyRequests off\n    ProxyPass / http://k3sserver:8080/ upgrade=any\n    ProxyPassReverse / http://k3sserver:8080/\n\n    RewriteEngine on\n\n    RewriteCond %{HTTP:Connection} Upgrade [NC]\n    RewriteCond %{HTTP:Upgrade} websocket [NC]\n    RewriteRule /(.*) ws://k3sserver:8080/$1 [P,L]\n\n    # Custom log file for SSL\n    ErrorLog /var/log/apachehttp/coder/error.log\n    CustomLog /var/log/apachehttp/coder/access.log combined\n\u003c/VirtualHost\u003e\n\n\u003cVirtualHost *:80\u003e\n    ServerName *.coder.mydomain.com\n    RewriteEngine On\n    RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}\n\u003c/VirtualHost\u003e\n```\n\n* Copy and update at your convenience [~/apachehttp/httpd.conf](apachehttp/httpd.conf) configuration file.\n\n* Create a file [~/apachehttp/docker-compose.yaml](apachehttp/docker-compose.yaml) with the following content:\n\n```yaml\nservices:\n\n  httpd:\n    container_name: httpd\n    image: httpd:latest\n    volumes:\n      - ./httpd.conf:/usr/local/apache2/conf/httpd.conf\n      - ./conf:/usr/local/apache2/conf/sites\n      - /ssl:/ssl\n    restart: always\n    ports:\n      - \"80:80\"\n      - \"443:443\"\n    networks:\n      - reverseproxynetwork\n\nnetworks:\n  reverseproxynetwork:\n    name: reverseproxynetwork\n    external: true\n```\n\n* Create and start Apache HTTP container:\n\n```console\n$ cd ~/apachehttp\n$ docker compose up -d\n```\n\n## Run\n\n* Open the https://coder.mydomain.com URL with your favorite web browser.\n\n![Coder is running on K3s](./static/coder-sigin.png)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmickaelbaron%2Fcoder-k3s-guide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmickaelbaron%2Fcoder-k3s-guide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmickaelbaron%2Fcoder-k3s-guide/lists"}