VSCode server on Azure Ubuntu VM. A step by step guide
Tags: vscode, node, azure, bofh
Published: 2021-05-09 12:09:31

In this article I'll explain step-by-step on how to create your own VSCode server running on a VM in Microsoft Azure.
You do not need Azure, you can also use this guide on a VM on a home-server, any other cloud provider or a VM provided by your employer.

## What to expect from this guide?

After following these steps, you'll end up with a development server which you can use to work on Node.js frontend and backend projects. You could probably use it for other stacks. From my experience working with this setup is almost the same as running VSCode on your own machine, except it's not on your local machine.

You can connect your local VSCode editor with the VSCode server using SSH. Code completion, 'go to definition', debugging, running your code, even the terminal, it's all the same. If you ever worked from VSCode on Windows with locally running containers or WSL ; it's exactly the same.

Your development server will connect to your *Global Area Network* using [ZeroTier](https://www.zerotier.com/). This makes it easy to connect without the need to change local configuration each time you start the server and it gets a new IP-address. Your development machine will be available from a local network. Nice bonus; you can completely fence the VM behind a firewall and still have SMB access.

*Why would you want to run VSCode from a server?*

Couple of reasons

- **Resources**; a fairly complex webapp can contain a lot of files and do a lot of resource hungry stuff. My main development machine is a Macbook Pro 13'' from 2019. It has an Intel Core i7 and 16 GB of RAM. Should be enough, right? While working, most of you have other stuff running; Slack or other chat apps, an email client, your browser (most webdevs have several running at once), a music player. My Macbook does throttle a lot and gets pretty hot when working on a fairly large codebase in TypeScript. Using a dedicated VM will result in faster transpiling, faster response when testing your app, faster code completion and overall a more productive IDE/text editor.
- **Security**; having the code I write for my employer sitting on a system from my employer is safe. If you're freelancing you can even use this as a USP; "everything I code for you is on systems you own".
- **Flexibility;** you can work from whatever machine you have within reach, as long as it is connected and has VSCode. Let me correct that; *as long as it has a decent browser*. You can use a normal local VSCode instance to connect to your VSCode server, I'd recommend this as daily driver. However; since VSCode is a webapp you can also use your browser to connect to your VSCode server.  You can use a Chromebook, or even an iPad with a keyboard.  You have the full functionality you'd have when you use the VSCode application.
And since your code is not actually on the machine you're using it does not really matter if it's a company laptop or personal laptop.

## Costs

Free Microsoft credits aside, this VM will probably set you back around $25,- per month. You can shutdown the vm when you are not working, but you will need some grunt to comfortably run VSCode server. I use the *Standard B2ms (2 vcpus, 8 GB memory)* VM-size which costs $70,- per month. That's steep, and you might get the same results use the B2s instance, which has 2 cores, 4GB of RAM and 16GB SSD and will set you back roughly $15,- per month. If you'd left it running a full month you'd be paying $35,- per month. I'll be testing the B2S instance upcoming week and will report back on my findings.

_Update_ : after one morning working on the B2s instance I ran into memory issues. I had 3 projects open, 2 of them running (a serverless backend and a isomorphic frontend), I noticed the editor getting sluggish and `top` revealed there was no RAM left. Since by default the Azure Linux VM's have no swap enabled the VM was slowly crashing. So I created a swap-file using the procedure described at the end of this article and I'm currently working with 4GB RAM and 5GB of swap.

## Prerequisites

I assume you have all of the next items in place, or know a decent amount about;

- An Azure account, either with credits or a valid creditcard and reasonable understanding of what Azure is, how to use it and the way the webapp works.
- Comfortable with Linux terminal, you know how to create a SSH-key, install packages
- You already have a ZeroTier account and the ZeroTier client installed on your own machine. There are a lot resources explaining setting up ZeroTier, so use the-Google for that (or read [this](https://www.stratospherix.com/support/setupvpn_01.php))
- If you want to secure the webinterface with an SSL certificate; a (sub)domain of which you can update the DNS records(recommended!)

## Let's get started!

- Create a Virtual Machine in Azure in the region close to where you are, select whatever type you want and what youre credit card allows. I will be setting up a B2s instance, with 2 Cores and 4GB or RAM.
    - Select Ubuntu Server 21.04 - Gen1 as image.
    - Use SSH public key authentication and use the key Azure creates or use a key you already have in place. Please note; you can not use ed25519 keys for now. Don't forget to enter a username to login.
    - Network; for now allow SSH (22) and port 80 (service: http).
    - Disks; depending on your needs you can add extra data disks.  For my situation the standard amount of 32 GB is enough.
    - Management; Enable auto shutdown and set a time that's convenient for you, I use 9 pm, the likelihood of me still working at 9 pm is very slim.
- When the VM is up and running, connect to it with SSH. You can use the IP found at "Overview" in the Azure portal. If the SSH key you used is not the default key you can use the `-i` argument to switch sshkeys like so: 

```
ssh -i ~/.ssh/id_rsa user@address
```

- First thing I usually do is add my ed25519 key to ~/.ssh/authorized_keys (optional)
- Second thing; update the system;

    ```bash
    sudo apt-get update && sudo apt-get upgrade
    ```
- Configure `max_user_watches`. If you keep this at it's default value you might get errors like this `Error: ENOSPC: System limit for number of file watchers reached` when you use node_modules like `nodemon` or other file watchers in larger codebases. You can increase the value for `max_user_watches`

    ```bash
    echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
    ```
- Now install ZeroTier:

    ```bash
    curl -s https://install.zerotier.com | sudo bash
    ```

    and add it to your virtual network ;

    ```bash
    sudo zerotier-cli join <your network-id from ZeroTier>
    ```

    Authorize the client at the ZeroTier website and give it a static IP (by adding an address to the machine by hand on the website instead of letting the site decide).

    Disable the ubuntu firewall

    ```bash
    sudo ufw disable
    ```

    *Try to connect to the VM with SSH on its ZeroTier address before proceding. It could take a while before the virtual network is up & running, also after rebooting!*

- Set a password for your user, you will need it to install packages from VSCode terminal

    ```bash
    sudo passwd [your username]
    ```

- Optional: install zsh and oh-my-zsh
    - Install ZSH:

        ```bash
        sudo apt-get install zsh
        ```

        Set Zsh as default shell;

        ```bash
        sudo chsh -s /bin/zsh <your username>
        ```

    - Install oh-my-zsh, and the auto complete plugin (you will like it)

        ```bash
        sh -c "$(wget -O- [https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh](https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh))"
        git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
        ```

    - Add the plugin to the list of plugins for Oh My Zsh to load (inside `~/.zshrc`):

        ```bash
        plugins=(git zsh-autosuggestions)
        ```

- Download VSCode server from [https://github.com/cdr/code-server/releases](https://github.com/cdr/code-server/releases)  and install it

    ```bash
    wget -q https://github.com/cdr/code-server/releases/download/v3.9.3/code-server_3.9.3_amd64.deb
    sudo dpkg -i code-server_3.9.3_amd64.deb
    ```

- Setup systemctl

    ```
    systemctl --user start code-server
    systemctl --user enable code-server
    ```

    *Note; these commands are not run as sudo*

    I've noticed that my code-server was not started at boot, because it's tied to my user-account. To allow starting at boot, use the following command;

    ```bash
    sudo loginctl enable-linger <your username>
    ```

- Configure authentication by editing `~/.config/code-server/config.yaml`. Set up a strong password, you won't need to change the IP-binding since we'll be setting up a reverse proxy.
- If you don't want to use the web interface and will only use SSH from another VSCode app you're basically ready, skip next steps to finish up.
- If you do like to use VSCode from a browser, move on to install NGINX and optionally Let's Encrypt.
- Let's Encrypt
    - You need to set up a (sub)domain with an A record that points to the IP address assigned to the VM. For this tutorial I set up [vscode.syntacticsugar.nl](http://vscode.syntacticsugar.nl) with a TTL of 60 seconds to ensure it's available quickly. You can change the IP to the IP you've assigned from ZeroTier in a later stage.
    - Install Let's Encrypt:

        ```bash
        sudo apt install certbot -y
        ```

    - Request the certificate:

        ```bash
        certbot certonly --standalone --agree-tos -m <enter your email> -d <the domain you set up>
        ```

        *This could fail the first few times as DNS updates tend to be slower whenever you need them to be fast.*

    - When the certificate has been successfully created, change the DNS to the IP address you assigned in ZeroTier.
- NGINX reverse (SSL) proxy
    - Install NGINX:

        ```bash
        sudo apt install nginx -y
        ```

    - Create config:

        ```
        cd /etc/nginx/sites-available/
        sudo vim code-server
        ```

    - If you have setup SSL, paste this config :

        ```
        server {
         listen 80;
         server_name <YOUR DOMAIN>;
         # enforce https
         return 301 https://$server_name:443$request_uri;
        }

        server {
         listen 443 ssl http2;
         server_name <YOUR DOMAIN>;

         ssl_certificate /etc/letsencrypt/live/<YOUR DOMAIN>/fullchain.pem;
         ssl_certificate_key /etc/letsencrypt/live/<YOUR DOMAIN>/privkey.pem;

         location / {
         proxy_pass http://127.0.0.1:8080/;
         proxy_set_header Host $host;
         proxy_set_header Upgrade $http_upgrade;
         proxy_set_header Connection upgrade;
         proxy_set_header Accept-Encoding gzip;
         }
        }
        ```

    - Activate the VHost;

        ```bash
        sudo ln -s /etc/nginx/sites-available/code-server /etc/nginx/sites-enabled/
        ```

    - Check the config

        ```bash
        sudo nginx -t
        ```

    - If all's fine, restart the services;

        ```bash
        sudo systemctl restart nginx
        sudo systemctl enable nginx
        ```

    - Check if you can reach code-server from your browser by going to [https://yourdomain](https://yourdomain)
- Harden the firewall of your VM in the Azure Portal in the Networking Section. If you dare to rely on your ZeroTier connection you can disable SSH completely. If you're not the daring type consider only allowing connections to SSH from your own company or home IP. Also remove the rule for port 80.
If you are planning to use VSCode from a browser outside ZeroTier you can leave port 80 and add an allow rule for port 443. This is NOT recommended from a security point of view.
- Optional: Next steps are for those that use Node.js, if you're into something else, please setup the tools you need in order to get some work done
    - Install NVM (node version manager, check [https://github.com/nvm-sh/nvm](https://github.com/nvm-sh/nvm) for the latest version)

        ```bash
        curl -o- [https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh](https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh) | bash
        ```

    - Setup path in .zshrc;

        ```bash
        nano ~/.zshrc
        ```

        Paste the following config at the end of the file;

        ```bash
        export NVM_DIR="$HOME/.nvm"
        [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
        [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
        ```

    - log out and log in again
    - Now install the Node.js version you want to use (to list al available versions, use `nvm ls-remote`

        ```bash
        nvm install v12.22.1
        ```

    - Install the global packages you might want to use
- Let's setup VSCode on your local machine to connect to your VSCode server. 
    - Install the [VSCode Remote Development extension pack](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack)
    - Open VSCode and click "Open Remote window" at the most bottom left corner
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n4c2hd9k220if6mmlc1p.png)

    - Select `Open SSH Configuration File`
    - Select the config file in your home directory/.ssh
    - Add the following configuration (and modify it to reflect your environment)

        ```bash
        Host [the hostname you used to create ssl or the Zerotier IP address ]
        HostName [the ZeroTier Ip address]
        User [your username]
        IdentityFile ~/.ssh/id25519 [or the SSH private key file you use to connect]
        ```

    - Now click the same button `Open Remote window` , select  `Connect to host` and select the host you just added.
    - If all is fine you should get an empty VSCode window, the button has changed and shows `SSH: [hostname]`. 
    **Congrats; you are now working on your VSCode server!**
- Next step; install plugins.
    - Open the plugins tab and scroll through the windows with locally installed plugins. Click `Install in SSH: [hostname]` to install them on your VSCode server.  You probably need to close VSCode and reconnect.
- Optional and highly recommended; install **[Kite autocompletion](https://www.kite.com/)**.  Kite adds AI powered code completions to your code editor, giving you superpowers. You do not need to install kite on your local machine and kite will work if you use your browser to work in VSCode.

    ```bash
    bash -c "$(wget -q -O - [https://linux.kite.com/dls/linux/current](https://linux.kite.com/dls/linux/current))"

    systemctl --user start kite-autostart
    ```

    You need to restart vscode server after installing Kite; 

    ```bash
    systemctl --user restart code-server
    ```
![Kite giving suggestions from the webinterface](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nkqvammzaedjubjcsy35.png)

## Tips and tricks and daily usage

### Getting started in the morning

I have not found an easy way to autostart my VM every morning. To be honest; I don't think I need that either. I have days with back to back meetings and I don't want the VM burning to my Azure credits without me using it. 
So I login to the Azure portal and start it manually every time I need it. When it's up and running I connect my local VSCode app and hack away. 
_Update_: I stumbled upon the Azure App for iOS, this app makes it very easy to start your development VM.

### Portmapping

If you run a project using node you'd normally fire up a browser and navigate to [http://localhost:port](http://localhost:port) . *Using VSCode server is exactly the same!* VSCode will create SSH tunnels for you so you can connect to localhost:portnumber. So you won't run into CORS issues or other strange behaviour. 

### Opening a VSCode window from the VSCode terminal

Imagine; you are working on a frontend from on your VSCode server from a local VSCode instance using SSH. You realize you need to check some stuff in another project, which has been cloned into another folder on your VSCode server. You can `cd` to that project using the terminal within VSCode and fire up a new editor by simply typing `code .` 

### Finishing up for the day

You had a productive day writing elegant code and finishing several tickets. You're ready for a quiet evening doing other stuff. Before closing the lid of your laptop be sure to save ALL files in VSCode and commit & push your work. Your VM will shut down later tonight which could lead to data loss. I have not run into this, but better safe than sorry right?

## Known Issues

- It could take a while for ZeroTier to connect after booting the server. If you have issues ZeroTier not connecting at all try to login using SSH with the dynamic IP assigned by Azure and run ZeroTier join command; `sudo zerotier-cli join <your network-id from ZeroTier>`
- The VSCode webinterface might work better if you use Chrome.
- Not enough memory? Enable swap on your Azure VM;
    - edit `/etc/waagent.conf` 
    - add or uncomment these lines (set `SwapSizeMB` to match the amount of RAM your VM or more) :
        ```
ResourceDisk.Format=y
ResourceDisk.Filesystem=ext4
ResourceDisk.MountPoint=/mnt/resource 
ResourceDisk.EnableSwap=y
ResourceDisk.SwapSizeMB=4096
        ```

    - reboot your VM and you should see swap memory in `top`

## Questions? Praise? Complaints?

Email :  [i.toby@wearetriple.com](mailto:i.toby@wearetriple.com)

Twitter: [https://twitter.com/buttonfreak](https://twitter.com/buttonfreak) [](mailto:i.toby@wearetriple.com)