Skip to main content
Warning: This document is old! It is likely wrong in some important way.

VSCode-Remote

Deploy a remote development system for VS Code’s Remote SSH on Fly.

Rationale

When you get the need to develop but there’s no machine to hand that’s quite powerful enough, your next step may be to roll yourself a cloud instance and develop on there. With tools like VS Code’s Remote SSH mode, remotely working with a development machine in the cloud can be a joy. But then you have to go through the entire provisioning process every time you want a fresh machine to work with. Which is why using Fly can make it simpler. This example is all about making that process completely automated. At the end of deploying it, you’ll have a Fly instance with attached disk storage, running Ubuntu, accessible by you and using certificate-based authentication, with Docker and whatever packages you select.

Using

Get the code by git cloning the repository:
Then cd into the vscode-remote directory and run the configure script.
We’ll pause here to review the only interactive part of the process.
  • First, there’s a request for an app-name which is used for the instance’s hostname. If you don’t need a particular name, just hit return and Fly will generate an appname.
  • That’s followed by a request for an organization. Just hit return to put it into your personal organization.
  • Then there’s a request for the number of gigabytes of disk space you want attached to the instance. This will be your working space. Hit return to get the minimum (and default) of 10GB.
  • If you want Docker’s daemon and tools pre-installed, answer Y at the next question.
  • You can add other Ubuntu packages to be pre-installed at this point. Hit return for none, or enter their names in a space-separated list.
Why is this pre-installation important you may ask. Remember that this is the image that we will start with and the image that Fly will reload when an instance is restarted or moved. Not pre-installing it means that, although you could install something, it would be completely transitory and disappear when the instance was restarted. Unless you made provision to install things on the persistent disk space (not simple for system packages). So thats why we ask about Docker and extra packages. From this point on, the deployment process happens without intervention.

Creating the configuration

The script starts work by creating a template (import.toml) and using that to create a new Fly app. This template is custom created and includes copies of the public keys of the user which are then used to authenticate SSH logins to the app later. This then automatically configures the app to work with SSH on port 10022 externally and port 22 internally.
The createdfly.toml` file already has an entry in it for a persistent volume called data. That now needs to be created.

Creating a persistent volume

The script gets the region the app was created in (here lhr) and creates the volume in it.
It is now able to go ahead and build the image for deployment.

Building the image

At this point, the script runs fly deploy which itself does two things; build the app’s image and deploy the results. The building is done by processing the Dockerfile.
There’s a lot more output that comes here with the creating and installation of all the packages required, along with any other packages requested. We’ve skipped it here, but rest assured we end up here next:
The next part sees the new image first pushed to the fly.io repository, and then after being processed and optimized, deployed to the Fly infrastructure.
The very last thing we are told is the address we need to give to VS Code when we do “Remote-SSH -> Connect to Host”. Open VS Code, bring up the command line (Command/Control-Shift-P), enter remote-ssh and select Connect to Host. You’ll be prompted to Select configured SSH host or enter user@host. We’ll do the latter, and enter (as per our example) dj@crimson-bush-6560.fly.dev:10022. You’ll be prompted to confirm the fingerprint of the machine you are connecting to (hit return) and then a new VS Code window will appear, with everything - file system, language server, tools and terminals running on the remote machine.

Inside the process

Now, at this point, if you’re happy launching your own VS Code SSH instances on Fly and don’t need to know more you can go. If you are curious on how this example is built, then read on.

The Dockerfile

The heart of many Fly apps is the Dockerfile, which creates the image that will be sent up to Fly to run. In this case, it uses Ubuntu Bionic as a base then:
  • installs a number of essential packages (SSH server) and anything selected by the user as an extra package.
  • creates a user with a selected name.
  • gives that user sudo privileges.
  • installs Docker components if they were requested
  • copies over SSH server configuration and keys
  • sets up the image to run
The important part here is that variables, USER, USE_DOCKER and EXTRA_PKGS are used to configure some of that build behavior.

The Script

The configure.sh script is responsible for getting the values of those variables. It asks the user running it for settings for the various values. It also asks for an appname (as $appname), an organization (as $orgname), and a disk size for the attached volume. Finally, it gathers the user’s SSH public keys into a single string called AUTHORIZED_KEYS. With all the values acquired, the script can create the app.

Creating with a Template

To create the app, the script writes out a file called import.toml. It’s a template for what we would like in our application when we create it. It sets up SSH’s port 22 to appear as port 10022, adds a mount for a disk volume, and passes SSH AUTHORIZED_KEYS as an environment variable. Then it runs:
This creates an app with the user’s requested app name and organization and then imports the import.toml file to configure it. It places the app in the user’s personal organization. Finally, an --overwrite flag means this command will overwrite existing fly.toml files without querying the user.

Finding the Region/Creating a Volume

Now, this script needs to create a volume in the same region as the app to provide storage for it. While humans can easily read the the output saying which region the app has been created in, it’s currently a little harder for scripts. That’s where this snippet comes in:
It runs the fly regions list command but with the --json flag which makes the output somewhat more processable. It then uses awk (a fine Unix command) to extract out the region code from the line which reads "Code": "lhr",. It’s that value it saves into the REGION variable ready for when it runs:
Which creates the storage volume.

Deploying with Docker Arguments

At this point in the script we are ready to deploy with fly deploy but we still have our USER, USE_DOCKER and EXTRA_PKGS arguments to pass. The command line looks like this:
The --build-arg flag takes a name=value pairing as its parameter. The first build-arg sets USER to the value of the current user’s name. The second build-arg sets EXTRA_PKGS to the value of the $extrapackages variable; notice that it is surrounded in quotes are the list of extra packages will likely contain spaces which the shell would process. Finally, we have $usedocker on its own, but that’s because, if you scan to the top of the script, you’ll see that it the user answers “Yes” to the question, the value of $usedocker is set to “—build-arg USE_DOCKER=y” (if they answer no, then $usedocker is just an empty string). As you can see, there’s a lot of ways to build up your build arguments. Consult your shell and Dockerfile references for more. Once run, the command will build, using the Dockerfile and build-args, an image which will then be deployed onto the Fly infrastructure. The only thing left is the tell the user how to connect and what address to use.
The fly info --host is an easy way to get the host name of your deployment for connection strings.

Discuss