---
title: Code-Server
description: Install code-server to run VS Code in your browser from your homelab, mount folders and expose it securely with SWAG.
---


:ellipsis{left=0px width=40rem top=10rem blur=140px zIndex=60}

[code-server](https://github.com/linuxserver/docker-code-server) is a container that lets you access [VS Code](https://code.visualstudio.com/) via a web UI in a Linux environment. It's literally VS Code and your projects in your pocket, available anywhere.

![code-server](https://github.com/coder/code-server/raw/main/docs/assets/screenshot-2.png)

## Installation
::note{to="https://docs.linuxserver.io/images/docker-code-server/"}

For this setup, we’ll use the **image maintained by LinuxServer.io**.
::

::file-tree
---
tree:
  /:
    - srv:
      - docker:
        - code-server:
          - compose.yaml
          - .env
          - config/
    - (any folder you want to mount in VS Code)/
---
::

::steps{level="3"}
### Deploy the stack

Open Dockge, click on `compose`, name the stack `code-server`, and paste the following:

```yaml [compose.yaml]
---
services:
  code-server:
    image: lscr.io/linuxserver/code-server:latest
    container_name: code-server
    environment:
      - PUID=${PUID}
      - PGID=${GUID}
      - TZ=Etc/UTC
      - HASHED_PASSWORD=${PW}
    volumes:
      - /srv/docker/code-server/config:/config
    # add folders to mount in VS Code
    # - /path/to/folder:/folder
    ports:
      - 8443:8443
    restart: unless-stopped
```

::tip{icon=""}
✨ Add the Watchtower label to each container to automate updates

    ```yaml [compose.yaml]
    services:
      code-server:
        #...
        labels:

          - com.centurylinklabs.watchtower.enable=true
    ```
::

### Set your environment variables

Choose a password and generate its hash:

```bash [Terminal]
echo -n "yourpassword" | npx argon2-cli -e
```

Save the result carefully. Find your PUID and GUID with:

```bash [Terminal]
id yourusername
```

Fill in the `.env` file with the values you found, for example:

```properties [.env]
PW='$argon2i$v=19$m=4096,t=3,p=1$wST5QhBgk2lu1ih4DMuxvg$LS1alrVdIWtvZHwnzCM1DUGg+5DTO3Dt1d5v9XtLws4'
PUID=1000
GUID=1000
```

::warning

__Note:__ Make sure to wrap the hash in single quotes `'`
::

Deploy the container and go to `http://yourserverip:8443`. Voilà, your code-server instance is up and running in the browser!

### Done !
::

::caution

__If it fails:__ check your firewall rules.
::

## Mount Folders
You can mount folders into VS Code by adding the relevant volumes in `compose.yaml` (or via Dockge), then redeploy the container.

```yaml [compose.yaml]
---
services:
  code-server:
    #...
    volumes:
      - /path/to/folder:/folder
```
Once inside VS Code, you'll have access to the mounted folder.

## Expose code-server with Swag
The whole point of such a solution is to access it remotely from any device. To do this, we’ll expose code-server via Swag.

::note

__Preliminary:__ We assume you’ve created a subdomain like `code.yourdomain.com` with a `CNAME` pointing to `yourdomain.com` in your [DNS zone](/general/networking/dns). Unless you're using [Cloudflare Zero Trust](/serveex/security/cloudflare), we also assume you’ve forwarded port `443` from your router to port `443` on your server using [NAT rules](/general/networking/nat).
::

::steps{level="3"}
### Add code-server's network to SWAG

In Dockge, go to the SWAG stack and edit the compose file to add code-server’s network:

```yaml [compose.yaml]
---
services:
  swag:
     container_name: # ...
      # ... 
     networks:           # Connects the container to a custom network 
      # ...           
      - code-server      # Name of the network defined in the stack
    
networks:                # Defines the custom network
  # ...
  code-server:           # Name of the network defined in the stack
    name: code-serveur   # Actual name of the external network
    external: true       # Indicates it’s an external network
```

::note

We assume the network name is `code-server_default`. You can verify that the connection works by visiting the SWAG dashboard at http://yourserverip:81.
::

Redeploy the stack by clicking “deploy” and wait until SWAG is fully operational.

### Create the subdomain.conf file

Inside the Swag config folders, create the file `code.subdomain.conf`.

::tip{icon="" to="/serveex/files/file-browser-quantum"}
✨ __Tip:__ You can use **File Browser Quantum** to navigate and edit your files instead of using terminal commands.
::

```bash [Terminal]
sudo nano /srv/docker/swag/config/nginx/proxy-confs/code.subdomain.conf
```

Paste the following configuration:

```nginx [code.subdomain.conf]
## Version 2023/12/19

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name code.*;

    include /config/nginx/ssl.conf;

    client_max_body_size 0;

    #if ($lan-ip = yes) { set $geo-whitelist yes; }
    #if ($geo-whitelist = no) { return 404; }
    if ($geo-blacklist = no) { return 404; }

    # enable for ldap auth (requires ldap-location.conf in the location block)
    #include /config/nginx/ldap-server.conf;

    # enable for Authelia (requires authelia-location.conf in the location block)
    #include /config/nginx/authelia-server.conf;

    # enable for Authentik (requires authentik-location.conf in the location block)
    #include /config/nginx/authentik-server.conf;

    location / {
        # enable the next two lines for http auth
        #auth_basic "Restricted";
        #auth_basic_user_file /config/nginx/.htpasswd;

        # enable for ldap auth (requires ldap-server.conf in the server block)
        #include /config/nginx/ldap-location.conf;

        # enable for Authelia (requires authelia-server.conf in the server block)
        #include /config/nginx/authelia-location.conf;

        # enable for Authentik (requires authentik-server.conf in the server block)
        #include /config/nginx/authentik-location.conf;

        include /config/nginx/proxy.conf;
        include /config/nginx/resolver.conf;
        set $upstream_app code-server;
        set $upstream_port 8443;
        set $upstream_proto http;
        proxy_pass $upstream_proto://$upstream_app:$upstream_port;
    }
}
```

Press :kbd{value="Ctrl+O"}, then :kbd{value="Enter"} to save, and :kbd{value="Ctrl+X"} to exit.

### Done !
::

That’s it! code-server is now exposed!

## Protecting code-server with TinyAuth
Add [TinyAuth](/serveex/security/tinyauth)'s forward-auth check directly to `code.subdomain.conf`, the same way as [the TinyAuth guide](/serveex/security/tinyauth#protecting-an-app-via-reverse-proxy):

```nginx [code.subdomain.conf]{26-38,41-42}
## Version 2023/12/19

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name code.*;

    include /config/nginx/ssl.conf;

    client_max_body_size 0;

    #if ($lan-ip = yes) { set $geo-whitelist yes; }
    #if ($geo-whitelist = no) { return 404; }
    if ($geo-blacklist = no) { return 404; }

    # enable for ldap auth (requires ldap-location.conf in the location block)
    #include /config/nginx/ldap-server.conf;

    # enable for Authelia (requires authelia-location.conf in the location block)
    #include /config/nginx/authelia-server.conf;

    # enable for Authentik (requires authentik-location.conf in the location block)
    #include /config/nginx/authentik-server.conf;

    location /tinyauth {
        internal;
        proxy_pass http://tinyauth:3000/api/auth/nginx;
        proxy_pass_request_body off;
        proxy_set_header Content-Length "";
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $http_host;
        proxy_set_header X-Forwarded-Uri $request_uri;
    }

    location @tinyauth_login {
        return 302 https://tinyauth.mydomain.com/login?redirect_uri=$scheme://$http_host$request_uri;
    }

    location / {
        auth_request /tinyauth;
        error_page 401 = @tinyauth_login;

        # enable the next two lines for http auth
        #auth_basic "Restricted";
        #auth_basic_user_file /config/nginx/.htpasswd;

        # enable for ldap auth (requires ldap-server.conf in the server block)
        #include /config/nginx/ldap-location.conf;

        # enable for Authelia (requires authelia-server.conf in the server block)
        #include /config/nginx/authelia-location.conf;

        # enable for Authentik (requires authentik-server.conf in the server block)
        #include /config/nginx/authentik-location.conf;

        include /config/nginx/proxy.conf;
        include /config/nginx/resolver.conf;
        set $upstream_app code-server;
        set $upstream_port 8443;
        set $upstream_proto http;
        proxy_pass $upstream_proto://$upstream_app:$upstream_port;
    }
}
```

::note{to="/serveex/security/tinyauth#exposing-tinyauth-with-swag"}

The `location /tinyauth` block runs inside SWAG's own container, so SWAG needs to be on TinyAuth's Docker network to reach it by name (`tinyauth` here). This should already be set up from **exposing TinyAuth itself**. If you run into an error, double-check SWAG's compose file still has that network attached.
::

::tip{icon=""}
✨ __Tip:__ You can protect this app with [Authentik](/serveex/advanced/authentik) instead of TinyAuth by opening `code.subdomain.conf` and uncommenting the lines `include /config/nginx/authentik-server.conf;` and `include /config/nginx/authentik-location.conf;`. Don’t forget to [create an application and provider in Authentik](/serveex/advanced/authentik#protecting-an-app-via-reverse-proxy).
::
