Using Your Class Virtual Machine

Every student in the class will have their own virtual machine (VM) to do work. We highly recommend that you use your class VM to work on the in-class exercises and take-home projects. The VMs have the Linux OS and and some other software installed for you that will make getting started easier. Also, we (the teaching staff) have access to all of the VMs and can help you in case something goes very wrong.

SSH Access To Your VM

Once you have provides the instructors with your TACC account and your VM has been created, you can use the following steps to ssh to it. For security purposes, the student VMs will be behind a firewall and will not be directly accessible to the public internet.

To access your VM via SSH, you will need to first SSH to the login VM. This year, the login VM will be located at: coe332-2026.tacc.cloud

Steps to SSH to Student VM:

  1. First, SSH to the login VM (coe332-2026.tacc.cloud) using your TACC username, password and MFA token.

  2. From coe332-2026.tacc.cloud, type ssh coe332-vm. This should connect you to your personal VM as user ubuntu.

You should see the prompt change in your terminal. The two steps are depicted below:

Step 1: SSH from your laptop to student-login:

[local]$ ssh username@coe332-2026.tacc.cloud
(username@coe332-2026.tacc.cloud) Password:
(username@coe332-2026.tacc.cloud) TACC_Token:
 Welcome to Ubuntu 24.04.3 LTS (GNU/Linux 6.8.0-90-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

 System information as of Fri Jan  9 12:07:19 CST 2026

 System load:  0.02               Processes:             140
 Usage of /:   11.4% of 37.70GB   Users logged in:       1
 Memory usage: 9%                 IPv4 address for ens3: 129.114.35.192
 Swap usage:   0%


 Expanded Security Maintenance for Applications is not enabled.

 0 updates can be applied immediately.

 Enable ESM Apps to receive additional future security updates.
 See https://ubuntu.com/esm or run: sudo pro status


 Last login: Thu Jan  8 14:09:53 2026 from 136.62.142.180
 jstubbs@bastion-9:~$
Step 2: SSH from coe332-2026.tacc.cloud to your VM:
[coe332-2026]$ ssh coe332-vm

 Welcome to Ubuntu 24.04.3 LTS (GNU/Linux 6.8.0-90-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

 System information as of Fri Jan  9 18:08:55 UTC 2026

 System load:  0.0                Processes:             183
 Usage of /:   2.5% of 153.94GB   Users logged in:       0
 Memory usage: 1%                 IPv4 address for ens3: 10.10.29.135
 Swap usage:   0%

 * Strictly confined Kubernetes makes edge and IoT secure. Learn how MicroK8s
 just raised the bar for easy, resilient and secure K8s cluster deployment.

 https://ubuntu.com/engage/secure-kubernetes-at-the-edge

 Expanded Security Maintenance for Applications is not enabled.

 3 updates can be applied immediately.
 To see these additional updates run: apt list --upgradable

 Enable ESM Apps to receive additional future security updates.
 See https://ubuntu.com/esm or run: sudo pro status


 Last login: Thu Jan  8 21:28:43 2026 from 10.10.29.250
 ubuntu@student-1:~$

Working With VSCode and the VM

We will be writing code in VSCode this semester, a modern Interactive Development Environment (IDE) with many advanced features for software engineering such as syntax highlighting, code completion and interactive debugging. Our setup will involve having VSCode running on your local laptop while all the actual code we write and execute will run on your dedicated VM. In this setup, you can think of VSCode somewhat like a “web browser” with the actual source code living on the VM.

There are a few advantages to this approach, including:

  1. Your code can be accessed remotely from different computers, and different individuals can access the running code (including the instructors and TAs, who can help troubleshoot issues);

  2. All students code will execute in the same environment (Linux OS with the same CPU cores and memory, etc.)

Installing VSCode on Your Laptop

Hopefully everyone had a chance to install VSCode onto their computer last time. If not, here are instructions again, for Windows, Mac, and Linux:

  • Linux – Follow the instructions here.

  • Mac – Follow the instructions here.

  • Windows – Follow the instructions here.

Remember, you only need to follow the first step to install the actual VSCode application. Next, we’re going to install the following additional extensions.

Installing the Necessary VSCode Extensions

Open the Extensions view by either clicking Extensions from the left navbar (the icon with two squares and a diamond) or by using the Ctrl+Shift+X (Linux/Windows) or Cmd+Shift+X (Mac) key combination. You will see the extensions organized into listed of “Installed”, “Recommended”, etc. You can also search for extensions by typing into the search box. Install the following extensions:

  • Python (from Microsoft) – Core Python development functionality.

  • Pylance (from Microsoft) – Advanced Python development features (requires the Python extension).

  • Black Formatter (from Microsoft) – Auto-format Python code (also requires the Python extension).

  • Remote-SSH (from Microsoft) – Provided support for developing code on remote servers using an SSH connection.

To install an extension, click the extension from the Extensions tab and then click “install”.

Warning

In general, make sure you are installing VSCode extensions from Microsoft or a trusted source. If you search for popular extension names, such as “python”, you likely to find multiple extensions.

Installing the Python extension.

Installing the Python extension.

VM Setup: The Python uv Tool

We’ll also be using Python uv, a modern tool for managing local installations of Python packages in virtual environments as well as entire python installations themselves. We will be combining uv with Docker later in the semester for highly reproducible, portable software distributions.

Installing Python uv

To install uv on your Ubuntu VM, run the following:

[coe332-vm]$ curl -LsSf https://astral.sh/uv/install.sh | sh

This one command should be all that is required to get uv installed. You can test the installation with the following command (note: you will need to exit the current SSH session and reconnect to get the uv script on your local path; alternatively, you can source our bashrc file):

[coe332-vm]$ uv

An extremely fast Python package manager.

Usage: uv [OPTIONS] <COMMAND>
. . .

Managing Python Installations with uv

One great thing about uv is that it can manage multiple Python installations, in addition to packages associated with a specific Python project. We can list the specific Python versions that uv finds installed on the local machine and the ones available for download with:

[coe332-vm]$ uv python list
cpython-3.15.0a3-linux-x86_64-gnu                 <download available>
cpython-3.15.0a3+freethreaded-linux-x86_64-gnu    <download available>
cpython-3.14.2-linux-x86_64-gnu                   <download available>
cpython-3.14.2+freethreaded-linux-x86_64-gnu      <download available>
cpython-3.13.11-linux-x86_64-gnu                  <download available>
cpython-3.13.11+freethreaded-linux-x86_64-gnu     <download available>
cpython-3.12.12-linux-x86_64-gnu                  <download available>
cpython-3.12.3-linux-x86_64-gnu                   /usr/bin/python3.12
    . . .

To install a specific Python, use:

[coe332-vm]$ uv python install <python_version>

For example, ensure Python 3.14 is installed with the following command:

[coe332-vm]$ uv python install 3.14

If all goes well, you should see a message:

Installed Python 3.14.2 in 1.48s
  + cpython-3.14.2-linux-x86_64-gnu (python3.14)

We’ll be using Python 3.14 throughout the semester.

Creating Projects with uv

The uv tool has the concept of a project, which allows you to manage the Python installation (i.e., version) together with any packages needed for the code you are writing. Projects are associated with git repositories. In general, you will want to create a new uv project for each git repository you work on.

To create a new project, simply issue:

[coe332-vm]$ uv init <project_name>

For example, let’s create an in itial uv project to hold our the work we do in class:

[coe332-vm]$ uv init class-work

This creates a new directory, class-work, in the current working directory that includes the following files:

  • .git/

  • .gitignore

  • .venv

  • .python-version

  • README.md

  • main.py

  • pyproject.toml

You might first notice that it created the .git directory and a .gitignore file – it has set up the directory to be a git repository. As mentioned, each uv project is intended to be a git repository.

It also created a pyproject.tonl file. The pyproject.toml file records specific metadata about the Python version and packages that your project depends on. Let’s take a look at the contents:

[coe332-vm]$ cat pyproject.toml

[project]
name = "class-work"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.14"
dependencies = []

Here we see tha name of the projecct (class-work), the version of the project (0.1.0 since we didn’t specify anything), and some other metadata. For instance, we see requires-python = ">=3.14" indicating that the project requires at least Python 3.14, but a higher version would be acceptable. At the moment, we also see that dependencies is an empty list — we haven’t specified any library dependencies yet. More on this momentarily.

You can think of the pyproject.toml file as providing a minimum specification of the environment required to execute your code correctly. Once any dependencies are installed however, uv records the exact requirements in a lock file. We’ll revisit the uv.lock file a bit later.

A Word on Python Packages

The Python programming language includes many modules and packages for common programming tasks as part of the standard library. These packages come with Python itself — once you have downloaded and installed Python, there is nothing additional you need to do to use the package: simply import it into your script and you can being using it. Examples include the json library, for working with JSON data (which we will use in the next class), the os package, for working with files and directories, and the re package for leveraging regular expressions.

At the same time, there are many more third-party packages that you can install to add additional functionality to your programs. For example, the requests package provides an excellent API for making HTTP requests, and the fastapi package provides a framework for developing HTTP services. Unlike packages from the standard library, third-party packages must be downloaded and installed before they can be used.

Many of these packages are managed through the Python Pakcage Index, abbreviated PyPI, and pronounced “pi-pi”. Packages come in multiple different versions and with their own dependencies, so managing the installation of the third-party packages a certin program or code base depends on can be a challenging task.

Adding Dependencies

You can use the uv tool to install and manage dependencies for your project. To add a package, use the command uv add <package_name>, where <package_name> is the name of the package as it appears on the Python Package Index (PyPI). You can specify a details about the version(s) of the package your project can work with – for example, a minimum and/or maximum version. These details get recorded in the pyproject.toml file when you execute the uv add command. Additionally, uv will actually install a specific version of the package. Details about the specific version are recorded in the uv.lock file, including the sha256 hash of the content of any package installed.

Note that adding a single package may result in downloading multiple packages if the package being added has dependencies. In that case, the toml file will include just the package specified to the add command, while the lock file will include an entry for every package installed.

Below are some examples of adding packages with uv. Use the examples to add fastapi, pydantic and uvicorn to your project.

[coe332-vm]$ cd class-work

# add a package
[coe332-vm]$ uv add fastapi

# or add multiple packages at once
[coe332-vm]$ uv add uvicorn pydantic

# check the results
[coe332-vm]$ cat pyproject.toml
[project]
name = "class-work"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.14"
dependencies = [
    "fastapi>=0.128.0",
    "pydantic>=2.12.5",
    "uvicorn>=0.40.0",
]

Take a look at uv.lock file generated:

[coe332-vm]$ cat uv.lock

version = 1
revision = 3
requires-python = ">=3.14"

[[package]]
name = "annotated-doc"
version = "0.0.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" },
]


. . .

[[package]]
name = "typing-inspection"
version = "0.4.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
    { name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
wheels = [
    { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
]

. . .

At the very top, we see metadata about the version of uv.lock itself (version 1, revision 3). Then we see information about our project. First, we notice that the Python version must be 3.14 or greater — this is the same as what was in the toml file.

But then we see a list of entries starting with [package]. These are all of the third-party packages that our code depends on. Notice that we see many packages that we did not specify, like annotated-doc and typing-inspection. Why is that?

For each package, uv not only records the exact version (e.g., 0.4.2 of typing-inspection) but also the sha256 hash of the package, the dependencies of that package, and other metadata. Thus, while the pyproject.toml file describes the minimum specification of packaged needed, the uv.lock file described the exact environment needed to reproduce the Python environment used with the project code. Thus, the uv.lock file is an essential tool for reproducible environments.

VSCode IDE via Remote-SSH Extension

Now we will connect to code installed on the VM using VSCode and the Remote-SSH extension.

The easiest way I have found to do this is to create an SSH config file with an entry for your VM, and then use that alias in VSCode.

For more details and alternatives, see the documentation for Remote SSH [1].

Instructions for Mac and Linux

  1. Install VSCode and SSH client on your machine, if not already installed.

  2. Install the Remote-SSH client. Go to Extensions (Ctrl+Shift+X), type “Remote-SSH” and click Install

  3. On your local laptop, edit the file ~/.ssh/config to contain the following:

Host 332-vm
    User ubuntu
    HostName 10.10.xx.yy
    IdentityFile ~/.ssh/id_ed25519
    ProxyCommand ssh -o 'ForwardAgent yes' <tacc_user_name>@coe332-2026.tacc.cloud 'ssh-add && nc %h %p'

In the above config file, be sure to replace:

  1. 10.10.xx.yy – replace this with the IP address of your actual student VM.

  2. <tacc_user_name> – replace with your TACC username (and remove the <>).

Here we have added an entry that will use a proxy command that will first SSH to coe332-2026.tacc.cloud before SSHing to the actual student VM.

Note

You need the 10.10.xx.yy IP address for your server in the above (i.e., the values for “xx” and “yy”). You can use the command ip addr on your student VM to get the address. Alternatively, look on the coe332-2026 server for a file ~/.ssh/config which should list your IP there.

Each entry in the config file instructs SSH about how to connect to that host. Once entered, SSH’ing to a host in the config is as easy as using the alias:

$ ssh 332-vm
Notes:
  • In the first line, the part after Host is the alias. You can use any name you like, but it should be memorable.

  • The string after User should be the remote account to connect with.

  • The part after HostName (i.e., the 10.10.xx.yy) should be the actual IP address of your student server.

Instructions for Windows

Some students have gotten the instructions above to work in Windows, but here, we provide an alternative that should work in Windows Powershell (note, not Windows Subsystem for Linux). The initial SSH key setup in this procedure is a little different.

  1. First, check if you already have SSH keys on your local machine:

$ ls ~/.ssh/id_*

If you see files like id_rsa, id_ed25519, or id_ecdsa, you already have SSH keys. Note which one you have (we’ll use id_ed25519 as an example, but use whichever key type you have).

If you don’t have SSH keys, generate one:

$ ssh-keygen -t ed25519 -C "your_email@example.com"

Press Enter to accept the default file location (~/.ssh/id_ed25519) and optionally set a passphrase.

  1. Add your public key to your student vm

You now need to copy your public key to the ~/.ssh/authorized_keys file on your VM. First, get your public key:

On Windows (PowerShell):

PS> Get-Content ~/.ssh/id_ed25519.pub | Set-Clipboard

Now, SSH to your VM using the two-step process described earlier:

[local]$ ssh username@student-login.tacc.utexas.edu
(enter password)
(enter MFA token)
[coe332-2026]$ ssh mbs-337

Once connected to your VM, add your public key to the authorized_keys file:

[coe332-vm]$ vim ~/.ssh/authorized_keys

In vim, press i to enter insert mode, paste your public key, then press Esc and type :wq to save and exit.

While you are here, get the IP address for your VM (you’ll need this for the SSH config in step 5):

[coe332-vm]$ ip addr | grep 10.10
10.10.xx.yy # Copy this IP address
  1. Create SSH config file

On your local laptop, edit the file ~/.ssh/config to contain the following:

Host student-login-jump
    HostName coe332-2026.tacc.cloud
    User your_tacc_username
    IdentityFile ~/.ssh/id_ed25519
    ForwardAgent yes

Host 332-vm
    HostName 10.10.xx.yy
    User ubuntu
    IdentityFile ~/.ssh/id_ed25519
    ProxyJump student-login-jump

For All Operating Systems

Confirm VSCode Can Read Your SSH Config File. First, in a VSCode window, open the Command Pallette (Ctrl+Shift+P) and type “remote-ssh: open SSH configuration file”, and then make sure that the path to the config file you edited above shows up.

4. Now, in a VSCode window, open the Command Pallette (Ctrl+Shift+P) and type “remote-ssh: Connect to host”, and then:

4a) You should see the alias appear (in my case, I have additional aliases, but the 332-vm is the one we want). If you do not see it, there is probably an issue with your configs.

Remote SSH drop down with aliases from the ssh config file

Remote SSH drop down with aliases from the ssh config file

4b) The first time, you will be prompted to select the OS type (choose Linux)

4c) Once you select the OS type, a new VSCode window will open up and connect to the machine.

New VSCode window with Remote SSH

New VSCode window with Remote SSH

Note

If you get a “connection timeout” error, you may need to increase the Remote SSH connection timeout in your VSCode settings. To do that, do the following: 1. Open the Command Palette in VS Code (Ctrl+Shift+P or Cmd+Shift+P on Mac). 2. Type and select “Preferences: Open Settings UI”. 3. Search for “Remote.SSH: Connect Timeout”. 4. Change the value from the default 15 to 30 (or higher, like 60).

Choose the Open Folder... option and select the /home/ubuntu directory to see the complete listing.

Remote listing of files on the VM

Remote listing of files on the VM

Configuring a Remote Python Environment

Finally, we’d like to point our VSCode instance at our uv environment. For that, we first need to install the Python extension on the remote (ssh) host. To do that,

  1. Open the Extensions view in the new window (Ctrl + Shift + X)

  2. Search for the Python extension (by Microsoft)

  3. Click Install in SSH: <host>

With the Python extension installed in the remote, we now need to do the following:

  1. Open the Command Palette (Ctrl+Shift+P) and search for Python: Select Interpreter.

  2. A list will display the Python environments available on the remote machine

  3. If not displayed, select the Enter interpreter path... option.

  4. Enter the path to the Python binary in the uv virtual environment for the project. This should be something like: <project_root>/.venv/bin/python3.14. For example, /class-work/.venv/bin/python3.14

A Simple Test of the Remote Python Setup

As a simple test of the entire setup, open the main.py and add the line:

from fastapi import FastAPI

If the remote environment is configured correctly, this should “type check” without any import errors since we added fastapi to the project dependencies in the pyproject.toml.

Turning on Type Checking

To get the most out of VSCode and static analysis, we recommend that you turn on type checking. For that, do the following:

  1. Open VS Code Settings (File > Preferences > Settings).

  2. Search for python.analysis.typeCheckingMode in the settings search bar.

  3. Change the value from off (the default might be basic or off depending on the setup) to basic or strict. a. basic provides standard checks. b. strict provides more comprehensive checks and is more likely to flag non-existent function imports.

Test it out by adding a line like the following:

from fastapi import foobar

This should highlight foobar with a red underline indicating that the symbol does not exist within the fastapi package.

Note

If you set type checking to strict, VSCode will flag the FastAPI line as not being used unless you reference it elsewhere in your code. Unused imports should be avoided.

Terminals via VSCode

Another nice aspect of VSCode is that you can open terminals directly in the editor, and when you are using the RemoteSSH extension, the terminals opened will be terminals on the remote machine!

To open a new Terminal use the Command Pallette (Cmd+Shift+P or Ctrl+Shift+P) and type “Terminal” which should present the option: “Terminal: Create New Terminal”. Select this option, and you should a new terminal panel open up at the bottom of your VSCode window.

A terminal within the VSCode IDE.

A terminal within the VSCode IDE.

You can see that the prompt contains ubuntu@student-1 letting you know it is connected to your student VM.