Visual Studio Code on iPad

Versions and tools used:

  • code-server version 3.0.0

  • Your own server

  • Your own domain

With Apple increasing their focus1 on making the iPad a viable device for work, it is time to revisit using my iPad as a workstation for programming.

I rely heavily on command-line tools and language specific tools (rust-analyser, node, ghcide, etc) for my day-to-day programming, and my current setup features:

  • Blink with mosh to a remote server.
  • Neovim and wemux on the remote server.
  • iSH to play around with very simple CLI needs locally on the iPad.

On my computer I use Visual Studio Code, and its long been a wish to get that running somehow on my iPad. This is an attempt to make VS Code available on the iPad, under the restrictions that we have to deal with.

💡

This setup unfortunately doesn’t eliminate the need for a server yet! We’ll have to dream of that day to come.

Enter code-server

code-server enables you to run VS Code on a remote server, and access it through a browser. While not ideal, this is at least one way to get VS Code onto an iPad.

First, SSH into your server, so that we can setup code-server. We are going to download the latest release from GitHub, and set it up. Checkout the latest release at https://github.com/cdr/code-server/releases, and pick the asset for linux_x86_64,

$ ssh [email protected]
$ wget https://github.com/cdr/code-server/releases/download/3.0.0/code-server-3.0.0-linux-x86_64.tar.gz
...
code-server-3.0.0-linux-x86_6 100%[==============================================>]  64.31M  3.47MB/s    in 9.4s
$ tar -zxvf code-server-3.0.0-linux-x86_64.tar.gz

You should now have a folder called code-server-3.0.0-linux-x86_64. Let’s rename it and make the put the executable on our PATH,

$ mv code-server-3.0.0-linux-x86_64 ~/.code-server
$ ln -s "$HOME/.code-server" /usr/local/bin/code-server

Try firing it up,

$ code-server
info  code-server 3.0.0
info  Server listening on http://127.0.0.1:8080
info    - Password is xxxxxxxxxxxxxxxxxxxxxx
info      - To use your own password, set the PASSWORD environment variable
info      - To disable use `--auth none`
info    - Not serving HTTPS
info    - Automatic updates are enabled

Neat! 🙂

Securing the setup for remote access

So far code-server is only listening for local connections, but we’d like to be able to use it on the go, from a browser on the iPad. This means we have to do a little extra work to secure our setup.

code-server covers how to do this in their FAQ, but skips the specific steps. Due to an issue with self-signed certificates on iOS, we cannot use these2, so instead we will opt for letsencrypt!

First we will install certbot, which will manage the certificate renewal on our server,

$ sudo apt-get install software-properties-common # for add-apt-repository
$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt install python-certbot-nginx

Because these certificates are managed under certbot, we’ll need to setup a script that will move the certificates to a location we want, so that our code-server does not need root permissions. We’ll do this with a deploy-hook, which runs after each successful renewal.

Let’s make a directory for the certificates. For convenience we will also export our domain name as an environment variables, to be used throughout the rest of the post (change vscode.example.com to your own domain),

$ mkdir .code-server-meta
$ cd .code-server-meta
$ export DOMAIN=vscode.example.com

Let’s set up the renewal script in /etc/letsencrypt/renewal-hooks/deploy/renewal.sh,

$ echo 'echo '\''#!/bin/bash
echo "Letsencrypt renewal hook running..."
echo "RENEWED_DOMAINS=$RENEWED_DOMAINS"
echo "RENEWED_LINEAGE=$RENEWED_LINEAGE"

if [[ $RENEWED_LINEAGE == *"'$DOMAIN'"* ]]; then
  cat $RENEWED_LINEAGE/privkey.pem > '$HOME'/.code-server-meta/key.pem
  cat $RENEWED_LINEAGE/fullchain.pem > '$HOME'/.code-server-meta/cert.pem
  systemctl restart code-server
  echo "code-server cert and key updated and restarted"
fi'\'' > /etc/letsencrypt/renewal-hooks/deploy/renewal.sh' | sudo bash && sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/renewal.sh

We’ll now set up our certificates by starting certbot. During this you will be asked for your email, and to agree to the terms of service.

$ sudo certbot certonly --standalone --preferred-challenges http -d $DOMAIN

This will create the certificates in /etc/letsencrypt/live/$DOMAIN. Check that everything works by doing a dry-run of the certificate renewal,

$ sudo certbot renew --dry-run

Excellent! You are now ready to launch your code-server instance using the keys for HTTPS,

$ code-server --cert ~/.code-server-meta/cert.pem --cert-key ~/.code-server-meta/key.pem --host 0.0.0.0
info  code-server 3.0.0
info  Server listening on http://127.0.0.1:8080
info    - Password is xxxxxxxxxxxxxxxxxxxxxx
info      - To use your own password, set the PASSWORD environment variable
info      - To disable use `--auth none`
info    - Automatic updates are enabled

A login screen should appear. Use the password that the server printed, and you are in! 🥳

Welcome screen in Visual Studio Code on iPad Code file in Visual Studio Code on iPad

Daemonizing the server

Currently we need to manually start the server every time we reboot our server. Instead of this, we’d like the code-server to be managed as a system service.

We’ll do this by:

  • Starting code-server with a fixed password.
  • Setting up a script to start code-server in a screen instance.
  • Letting systemd manage the start/stop of the service.

Passphrase

First let us set up our password for the code-server, so that we can login across reboots. We’ll do this by dropping a simple plaintext file inside $HOME/.code-server-meta.

This is under the assumption that you are the only one with access to the server. It is recommended that you put a unique passphrase for this service.

$ echo "MySecretPassword" > $HOME/.code-server-meta/passphrase.txt

Manage code-server in screen

We are going to put our script to manage the code-server instance in $HOME/.code-server-meta/service.sh.

$ echo '#!/bin/bash

case "$1" in
  start)
    # Create a screen in detached mode, called "code-server"
    screen -dmS code-server bash -c '\''PASSWORD=$(cat $HOME/.code-server-meta/passphrase.txt) code-server --cert $HOME/.code-server-meta/cert.pem --cert-key ~/.code-server-meta/key.pem --host 0.0.0.0'\''
    echo "Service started."
    ;;
  status)
    result=$(screen -list | grep code-server)
    if [ $? == 0 ]; then
      echo "code-server service is ON."
    else
      echo "code-server service is OFF."
    fi
    ;;
  stop)
    # Quit the "code-server" screen
    screen -S code-server -X quit
    echo "Service stopped."
    ;;
  *)
    echo "Unknown command: $1"
    exit 1
  ;;
esac
' > $HOME/.code-server-meta/service.sh && chmod +x $HOME/.code-server-meta/service.sh

Systemd service

Finally, we are gonna put our systemd service in /etc/systemd/system/code-server.service.

$ echo "echo '[Unit]
Description=Service to run code-server
After=network.target

[Service]
Type=oneshot
User=$(whoami)
ExecStart=$HOME/.code-server-meta/service.sh start
ExecStop=$HOME/.code-server-meta/service.sh stop
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
' > /etc/systemd/system/code-server.service" | sudo bash

Enable the script and start the service,

$ systemctl enable code-server
$ systemctl start code-server

Navigate to your domain on port 8080. Congratulations, you’ve now got a solid setup for editing code in your iPad browser 🎉


  1. https://www.apple.com/newsroom/2020/03/apple-unveils-new-ipad-pro-with-lidar-scanner-and-trackpad-support-in-ipados/.↩︎

  2. See issue code-server#1122 covering this.↩︎