- Multipass has been installed.
- VSCode (Visual Studio Code) has been installed.
- Git has been installed.
- Remote - SSH extension for VSCode has been installed.
- GPG-Indicator extension for VSCode has been installed (if you use GnuPG to sign your commits).
- You know how to use VSCode with a Git provider such Gitlab, GitHub, BitBucket, or SourceHut.
For example because your system does not support Hyper-V (for Windows host), or because you need VirtualBox for other reasons and this conflicts with the default hypervisor.
PsExec
from SysInternals installed. This can be installed from the Microsoft Store (https://apps.microsoft.com/store/detail/sysinternals-suite/9P7KNL5RWT25) or via Chocolatey.
- Create a cloud-init YAML file to provision the instance (e.g.
vscode-dev.yaml
) [Note 1]. - Launch Multipass using the
cloud-init
YAML file (e.g.vscode-dev.yaml
)-
Change to directory containing
vscode-dev.yaml
-
Execute the following command:
-
For Windows, in a PowerShell:
multipass launch -c 4 -d 40G -m 4G -n hugo-dev --cloud-init .\vscode-dev.yaml --timeout 2400 lts
-
For Linux, in a bash shell:
multipass launch -c 4 -d 40G -m 4G -n hugo-dev --cloud-init ./vscode-dev.yaml --timeout 2400 lts
See [Note 2]
-
-
-
Change to the location containing the PsExec command (unless you have added it to your path). For example:
cd 'C:\Users\DanielDickinson\AppData\Local\Microsoft\WindowsApps'
-
Forward a port (in this case
22222
) on the local machine to Multipass instance's SSHPsExec -s $Env:VBOX_MSI_INSTALL_PATH\VBoxManage.exe controlvm "primary" natpf1 "sshvs,tcp,,22222,,22"
-
Launch VSCode
-
From the 'Command Palette' (Window: Ctrl+Shift+P), select "Remote-SSH: Connect to Host…" (alternatively use the 'remote' icon in the bottom-left corner of the full VSCode window)
-
Scroll to the bottom of the list, and select "Configure SSH Hosts…"
-
Select the
.ssh/config
file in your home directory (Linux) or your user profile directory (Windows). -
Add a section such as:
Host mp-dev Hostname 172.44.32.88 Port 22 User ubuntu IdentityFile ~/.ssh/your-ssh-private-key AddKeysToAgent yes
Where
172.44.32.88
is the IP address of the instances (for non-VirtualBox-based Multipass), or127.0.0.1
for VirtualBox-based Multipass AND changePort 22
to the port you used above (Port 22222
in the example above). -
Repeat Step
2.
(only) -
Select the host you defined (
mp-dev
in the example) -
Wait while VSCode adds and launches the VS Code server on the remote.
-
You should now have VSCode connected to your Multipass instance. You can use it mostly the way you would a local instance (but see Remote:Overview, and Remote:SSH for more details.
- Create a new git repository on your hosting service of choice (e.g. Gitlab, GitHub, BitBucket, SourceHut, etc).
- Clone the project repository into your home directory (e.g.
as
mp-dev-workspace
) - In VSCode select
File|Open Folder
- Create a new folder named
.vscode
in the folder created by step2.
- Select
File|Save Workspace As…
- Save the workspace into the
.vscode
folder you created.
- Add the extensions you want to use locally (see Extension Marketplace).
- For the extensions you want in your remote workspace, on the
'Install in SSH: mp-dev-workspace
button in the
Extensionssidebar windows (where
mp-dev-workspace` is the name of your actual workspace). - In addition, for those same extensions, click on the settings icon (gear)
for each extension and select
Add to Workspace Recommendations
. - Select the
mp-dev-workspace
workspace from the list (for example), and clickOk
. - Commit the resulting changes to the
.vscode
directory and push you new commits to your hosting service. - Select
File|Close Workspace
. - Reopen the workspace.
- You extensions should now be present on the remote.
For any repositories you wish to include in your workspace, use them as Git submodules. This makes it much easier to sync difference devices (e.g a laptop and a desktop with both your extensions and your code/content).
Since VSCode does not automate the addition and use of submodules, you will need to note the following:
-
Do not add a repository to the workspace directory by cloning to the workspace directory. Instead use a command such as:
git submodule add --name cool-submodule \ -- https://gitlab.com/useryou/a-cool-submodule.git a-cool-submodule
-
Make sure you keep your submodules and the base repo commits up to date and pushed to your hosting service so that you can move your work to your other device(s) at any time.
-
If you clone the workspace repository to a new device using the VSCode GUI, you will so need to do the following:
- Open a terminal in your workspace on the new device
- Execute
git submodule update --init --recursive -- .
You should now have your full set of code on the new device.
-
If you clone the repository to a new device using the command line, use:
git clone --recursive …
- Packages to add (and related configuration) using
apt
: This is arguably the most important part as without it one only gets a bare-bones instance. - SSH public key for the private key you will use to access the instance. This helps secure your connection and makes it more convenient by using a public/private keypair instead of entering a password for every connection to the remote host.
- Files to add to the instance: This should be kept to a minimum due to size limitations (see [Note 1]). The author uses this for configuration on which he relies.
- Commands to run for additional provisioning:
In the example there are two main sections:
- Copying files from step
3.
to their final destination and setting permissions and owner:group correctly. The author uses this order becausewrite_files
copies the files to the instance before users are created, and most of the files he adds are intended for the default user. - Adding software for which the default
apt
repositories do not have the desired version (usuallyapt
is fairly old versions of packages likeGo
andNode.js
) or for which there is no version in the defaultapt
repositories.
- Copying files from step
In the example cloud-init YAML file you will notice sections like:
content: |
H4sIAAAAAAAAA5WTXU+DMBSG7/srTnAZV9B7TS+WuYgXc2aZ3phlKR9bG+lHaCEas/9uUeaYEJTL
vu+Th3OaQnNODSSa+ImGgPuIfgWiIr6oWkEhiF+IVlBx4ksqlY9Q9qZVYeHhebm7vV8TbxKtlgsc
ykp4CL1AYMCbNCV2YWiYB1uYTiHsKeAKNowbyBVNDbj0ZPhoyCOOqWG7RAmdZ5Yr2ZINMB0v/EIQ
Eq8pLyDQtaheoWMJU7cQ34ObKAVcmgLnKqE5PigccwnbG7AskwjgcbaJSAe4dt66OXoOaS6tPqM9
b2krWuCUWor1u2VK9pp7mH/Jf/bqOlvVKFX4veGA8UyMEvdf6mU7blKpRXDIVfzHuJfY4Ceac/R0
t9rNZ/NoUb9/zJTIcBmX0paYlQcVJDRh2Ql2zGa1JvW/gz4BNjw2aX4DAAA=
encoding: gz+b64
There two main reasons for use Base64 gzipped content in your cloud-init
YAML file:
- The file you want to include contains binary, or text data that does not
'play well' with YAML encoding. For example a
tar
file of a subdirectory. - To keep the size of the
cloud-init
file smaller. Many cloud providers impose a size limit (e.g. AWS was (is?) 16KB, OVH (OpenStack-based) was (is?) 32KB) on the userdata (cloud-init YAML in this case) one can pass to the instance being created. Using Gzip reduces the size of the file, and using base64 encoding ensures the resulting binary data is not mangled by the YAML parser.
Note: Be very careful not to include private information ('secrets') in this file in public repositories, as Base64 is just a different encoding, it is not encryption.
A final note for the Base64 gzipped content. In some cases the example does not provide actual data because the data would contain private information ('secrets') which do not belong in a public repository.
For example there is this placeholder:
Linux: Create file from existing ~/.gnupg with the following command:
tar -czf - ~/.gnupg | base64
and copy the output into this content section (with correct indentation)
instead of this instructional text.
I recommend copy vscode-dev.yaml
to vscode-dev-secrets.yaml
so that
the .gitignore
in this repo will prevent storing the version that includes
secrets.
- For
-c 4
you may wish to adjust the 4 (number of CPU cores to give the instance) depending on your hardware. - Likewise for
-m 3G
the3G
memory size may need to be adjusted for your use case and hardware. - Similarly,
-d 40G
the40 G
for the virtual hard drive may be too large for the storage you have, or too small for your development use case. - The timeout (
--timeout 2400
) means that the instance is allowed to take up to 40 minutes to finish initializing (includingcloud-init
). The default is300
(seconds = 5 minutes) which this author finds too short a time when usingcloud-init
to provision the instance, on his hardware.