Inessential Hyades

Hyades is a free service at MIT that provides website hosting and flexible compute resources. Some cool things you can host or run with Hyades include:

There are also some things that Hyades is not suitable for, such as:

Hyades is developed and maintained by SIPB, MIT’s student computing club, which you might know from Hydrant and Courseroad. Hyades is the modern successor to SIPB’s Scripts and XVM projects, which were groundbreaking when they were created back in the 2000s but nowadays mostly obsoleted by Hyades.

Lastly, Hyades is (we hope) easy to use! The only prerequisites you need are knowing how to SSH and how to edit a JSON file. With Hyades, you don’t have to worry about the annoyances of managing a server, and Hyades automatically handles:

Technical background

This is the part of the guide where we will throw a lot of technical jargon at you. Feel free to skip it, or read it if you want to gain a slightly better understanding of Hyades than “type some weird commands and the website magically appears yay”.

Hyades runs your websites and compute workflows as microVMs, which are very barebones and fast virtual machines. In contrast, traditional VMs include lots of features like virtual GPUs that are unnecessary for running websites. Note that Hyades does not use containers (such as Docker containers).1 Containers differ from VMs by sharing the same kernel as the host machine whereas VMs run a new kernel per VM. This means containers are faster and use less memory than VMs, but significantly less secure. In just the first half of 2026, there have already been several infamous Linux kernel bugs such as Copy Fail which can be used to break out of containers, but not VMs. MicroVMs offer the same security as a VM, but with a comparable performance overhead as containers, so it’s the best of both worlds!

Hyades stores microVMs on disk as Docker images, which are compressed archives containing the files for your microVM. (You might also see them called “OCI images” or “container images” which are just a genericized term for the same thing, like “Kleenex” vs. “tissue”.) Admittedly this is a bit confusing, since Hyades doesn’t run the Docker images as Docker containers but rather as microVMs. Hyades uses Docker images because they have a lot of existing tooling and there are lots of prebuilt Docker images available on the internet.

Logging in

Ironically for a website hosting platform, the Hyades console is not a website but rather a command-line shell accessible over SSH. You can either log in with a Kerberos ticket or an SSH key, although you must first log in with Kerberos in order to add your SSH public key.

First, open a terminal and obtain a Kerberos ticket (with USERNAME replaced with your Kerberos username):

kinit USERNAME@ATHENA.MIT.EDU

If you don’t have kinit on your computer, you can alternatively SSH into an Athena dialup which will obtain a Kerberos ticket automatically.

Now on the machine with the Kerberos ticket (either your computer or a dialup), SSH into the Hyades server:

ssh -K USERNAME@hyades.mit.edu

(Tip: If you’re on MIT Wi-Fi, you can omit the .mit.edu)

You should be greeted with this:

=== Welcome to the Hyades console ===

Type ? or help to show help
User guide: https://hyades.mit.edu/inessential.html

(hyades) 

Note that this is not a Bash shell and instead has its own set of commands. Type ? to show the list of commands and ? COMMAND to get documentation about a specific command.

To make future logins more convenient, you can add your SSH public key to Hyades using the editssh command, which edits your ~/.ssh/authorized_keys file. Make sure you add your SSH public key and not your private key. You don’t need to use a dialup to SSH into Hyades when using an SSH key.

To exit the console, hit Ctrl-D or type exit.

Hyades by example

Your first microVM

For our first example, we will set up Rustpad, a simple collaborative document editor, on Hyades. First, come up with a silly name for your new site, which will be accessible at https://NAME.sipb.cloud.

In the Hyades console, run:

create NAME docker.io/ekzhang/rustpad 3030

This command downloads the ekzhang/rustpad Docker image. Note that you must prefix Docker Hub images with docker.io since otherwise Hyades won’t know which site to download the image from (the world is larger than just the Docker Hub!). The 3030 tells Hyades that our website runs on port 3030. Also, note that this does not start the microVM yet. To actually start it, run:

start NAME

(Tip: There’s tab completion for both commands and arguments)

Now visit https://NAME.sipb.cloud and your site should be up!

You can get the current status of your microVM:

status NAME

And its logs:

logs NAME

Importantly, on Hyades, microVMs have no persistent state by default. Every time you start a microVM, it starts out with a pristine copy of the OCI image, and stop discards any files that have been modified. For Rustpad, when you stop the microVM, all the documents that you and your friends have edited on there are lost.

Persistent storage

Hold up! That doesn’t seem very useful if a website has its memory wiped all the time! Fortunately, we can store files that persist across stops and starts using volumes.

Rustpad can save documents on disk in its database, so we need to put its database in a volume. We also need to tell Rustpad to save its database there using the environment variable SQLITE_URI.

Run the edit command (or edit v to use Vim) and edit the "volumes" and "envs" like this:

{
    "rustpad": {
        "image": "docker.io/ekzhang/rustpad",
        "volumes": {
            "rustpad_db": "/db"
        },
        "envs": {
            "SQLITE_URI": "/db/rustpad.db"
        },
        "network": "",
        "ports": {
            "rustpad": 3030
        },
        "exposeports": []
    }
}

Now press Ctrl-X to exit and then restart Rustpad:

restart NAME

Now your documents will still be accessible even after a restart!

When you’re done with this part of the guide, you can delete the Rustpad microVM if you want:

delete NAME

This doesn’t delete any volumes attached to the microVM, so you should do that manually:

rmvol rustpad_db

WordPress

Simply run wordpress NAME. You might have to wait a minute, and then visit https://NAME.sipb.cloud/ to finish setting up your site.

Building an app locally

In this example we will build and deploy a very simple Python web app to Hyades. You’ll need to have Podman (recommended) or Docker installed on your machine.

Here is the web app, main.py:

from http.server import BaseHTTPRequestHandler, HTTPServer

class SimpleHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header("Content-type", "text/plain")
        self.end_headers()
        body = f"Hello, world!\n{self.headers.items()}"
        self.wfile.write(body.encode('utf-8'))

HTTPServer(("", 8000), SimpleHandler).serve_forever()

First, we need to write a Dockerfile, which is basically a glorified Bash script that builds the app in an isolated environment. Create the file Dockerfile with these contents:

FROM docker.io/ubuntu:26.04
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update
RUN apt-get install -y python-is-python3
RUN apt-get clean
COPY main.py /
ENTRYPOINT ["python"]
CMD ["main.py"]

To learn more about building Docker images and writing Dockerfiles, check out the Docker docs.

Now, build the app with podman build . -t forgejo.mit.edu/USERNAME/project-database. forgejo.mit.edu is a code hosting platform and container registry run by SIPB. (If you’re using Docker, run the same commands but with docker instead of podman.)

Now visit forgejo.mit.edu and sign in. Go to your user settings, click on “Applications”, and generate a new access token with access to all repos and “read and write” permission for packages. Copy the token, and run podman login forgejo.mit.edu using your Kerberos username and the token as the password.

Next, push the image to the MIT Forgejo using podman push forgejo.mit.edu/USERNAME/project-database. By default your Docker image will be public, but you can make it private by changing profile visibility to private.

Lastly, SSH into Hyades and create a new microVM:

create projectdb forgejo.mit.edu/USERNAME/project-database 5000

If your image is private, generate a new token with read-only access to packages and then run login in the Hyades console using your new token as the password. (You should avoid reusing the token from earlier so that Hyades doesn’t have write access.)

If you push a new image, run update on Hyades to pull and use the updated image. Hyades also automatically updates all images daily.

Phew! That was a lot just to deploy our simple app onto Hyades. But most of this uses standard Docker or Podman tools, so there’s plenty of resources available online.

Building an app using the Forgejo CI

Cool, we just locally built a Docker image for our app! But, it’s kind of annoying to do that all the time, so luckily the MIT Forgejo has a CI system that can run builds every time we do a Git push.

Let’s set up a CI workflow for the Python web app above. First, on the MIT Forgejo, create a new repository, clone it to your computer, and add the Dockerfile and main.py. Forgejo reads CI workflows from the directory .forgejo/workflows, so additionally create a .forgejo/workflows/main.yml with the following:

name: Build and push Docker image

on:
  push:
    branches:
      - main

jobs:
  build-and-push:
    runs-on: ubuntu
    steps:
      - name: Checkout
        uses: https://github.com/actions/checkout@v7

      - name: Build and deploy
        uses: https://forgejo.mit.edu/SIPB/hyades-action@main
        with:
          package_token: ${{ secrets.PACKAGE_TOKEN }}
          update_token: ${{ secrets.UPDATE_TOKEN }}

We also need to give the CI workflow permission to push to your packages and trigger an automatic update on Hyades. First add your access token from earlier as a CI secret by clicking on “Settings → Actions → Secrets”. Call the new secret PACKAGE_TOKEN. Now SSH into Hyades and run updtoken to get your UPDATE_TOKEN and add it to the secrets as well.

Finally, commit these three files and do a Git push. Now if you visit the “Actions” tab of your repo, you should see the CI pipeline running!

The with: block supports several optional parameters: - containerfiles: Build a Dockerfile other than ./Dockerfile - tags: Set a tag other than latest - If update_token is omitted, then an auto-update on Hyades will not be triggered

Multi-VM websites

Sometimes you might need to run multiple coordinated microVMs to host a complicated web app. A common example is if your app uses a standalone database like PostgreSQL. In this example, we will set up the Vikunja task manager using ParadeDB (an extension to PostgreSQL). The Vikunja docs site provides a Docker Compose configuration which we’ll adapt. First let’s create the two microVMs:

create vikunja docker.io/vikunja/vikunja 3456
create paradedb docker.io/paradedb/paradedb:pg18

Now we can edit their configs to add the necessary volumes and environment variables:

{
    "vikunja": {
        "image": "docker.io/vikunja/vikunja",
        "ports": {
            "vikunja": 3456
        },
        "volumes": {
            "vikunja": "/app/vikunja/files"
        },
        "envs": {
            "VIKUNJA_SERVICE_PUBLICURL": "https://vikunja.sipb.cloud",
            "VIKUNJA_DATABASE_HOST": "localhost",
            "VIKUNJA_DATABASE_PASSWORD": "changeme",
            "VIKUNJA_DATABASE_TYPE": "postgres",
            "VIKUNJA_DATABASE_USER": "vikunja",
            "VIKUNJA_DATABASE_DATABASE": "vikunja",
            "VIKUNJA_SERVICE_SECRET": "klajklewjrklmwlkamtklemerwlkm"
        },
        "network": "paradedb",
        "exposeports": []
    },
    "paradedb": {
        "image": "docker.io/paradedb/paradedb:pg18",
        "ports": {},
        "volumes": {
            "paradedb": "/var/lib/postgresql"
        },
        "envs": {
            "POSTGRES_PASSWORD": "changeme",
            "POSTGRES_USER": "vikunja",
            "POSTGRES_DB": "vikunja"
        },
        "network": "",
        "exposeports": []
    }
}

Note that we used the network field to add vikunja to the paradedb microVM’s network, since by default microVMs are completely isolated from each other. This adds a dependency for vikunja on paradedb so that Hyades will wait for paradedb to start up, then start vikunja, reusing the paradedb network. (If we had instead added paradedb to the vikunja network, then Hyades would start up vikunja which would throw an error and stop because it can’t reach the database, and paradedb would never start up.)

Now we can start up vikunja:

start vikunja

Because vikunja depends on paradedb’s network, Hyades will automatically start up paradedb first. It might take a minute for Hyades to pull the images and set up the database.

Exposing TCP ports

All our examples so far have been websites using HTTPS. We can also use Hyades to host networked services that use TCP or UDP, such as a Minecraft server. In this example, we’ll set up a relay for Syncthing:

{
    "relaysrv": {
        "image": "docker.io/syncthing/relaysrv",
        "ports": {},
        "volumes": {
            "relaysrv": "/var/strelaysrv"
        },
        "envs": {},
        "network": "",
        "exposeports": [
            22067,
            22070
        ]
    }
}

Note that we use exposeports instead of ports here. exposeports is DANGEROUS and exposes your service to the public internet, so make sure you fully understand the security implications before using it. Additionally, all Hyades users share the same port namespace, so you may have to use a different port if the one you want is already reserved.

Groups

So far, your microVMs and websites have been tied to your user, but what if you want to host a site for a club or student group? Hyades integrates nicely with mailing lists, so you can give everyone in a list access to a shared group account.

More specifically, Hyades uses Moira groups, but you can easily make a Moira mailing list also a group. Visit WebMoira, select a list, click “Edit”, and check the box “Moira Group”. (Alternatively you can use blanche.) Note that you must be an owner of the list to do this, and the group must not be hidden for Hyades to see its members.

Now back in the Hyades console, run:

entergp GROUP

This should drop you in a new Hyades console, but for your shared group account. Note that Hyades allows any member of the group, not just the owners, to access the group account, so you probably don’t want to use your club mailing list but rather a list with just the officers.

Working with volumes

As mentioned before, Hyades uses volumes for persistent storage. To list all your volumes, run:

lsvol

To edit a volume, first get an interactive shell on a microVM that uses that volume:

exec NAME bash

Once you’re in the shell, you may need to first install a text editor.

You can also edit a volume using tmprun ubuntu, which will spawn a temporary Ubuntu microVM with all your volumes mounted under /mnt.

To delete a volume, run:

rmvol VOL

Lastly, you can export and import volumes using expvol and impvol respectively. For example, to copy a volume from your account to a group account, run on your own machine:

ssh USERNAME@hyades.mit.edu expvol VOL | ssh USERNAME@hyades.mit.edu entergp GROUP impvol VOL

Quotas

Hyades has several per-user quotas:

Resource Quota
Number of microVMs 10
vCPUs 4
RAM 32 GiB
RAM (per microVM) 8 GiB
Disk 40 GiB
Groups 2

If you need a higher quota, email sipb-hyades-root@mit.edu.

MIT auth integration

Hyades integrates with MIT’s authentication system using Petrock, another SIPB project. This is a bit like Scripts’ port 444 certificates feature.

For any *.sipb.cloud site, visiting /hlogin (for instance, https://hyades-test.sipb.cloud/hlogin) will redirect the user to Petrock. Your site should additionally redirect /hlogin to a nice landing page for the logged-in user. Similarly, /hlogout will log out the user. By default, users will remain logged in for 14 days.

You can use the following HTTP headers to get information about a logged-in user:

Header Description
X-Forwarded-Anonymous true if the user is not logged in, otherwise false. If true, then the remaining headers will be filled with junk data!
X-Forwarded-Email The user’s email.
X-Forwarded-Affiliation The user’s affiliation.
X-Forwarded-Name The user’s full name.
X-Forwarded-Given-Name The user’s first name.
X-Forwarded-Family-Name The user’s last name.

See the Petrock docs for more details.

Full docs for hyades.json

Throughout this guide we’ve been editing the ~/.config/hyades.json file, which is basically docker-compose.yml but in JSON and much simpler. Here is the full documentation for that file:

Running a full Linux distro on Hyades

There are two main ways to run a full Linux distro on Hyades: bootable containers and NixOS. Since Hyades is based on ephemeral microVMs with persistent volumes, it’s not possible to use a traditional Linux distro with a persistent root filesystem on Hyades.

Some additional limitations include:

  1. microVMs on Hyades use a special, non-customizable kernel and you can’t load additional kernel modules.
  2. microVMs use TSI (transparent socket impersonation) for networking which only supports UDP and TCP, so low-level networking stuff like VPNs or raw sockets won’t work on Hyades.

Bootable containers

Bootable containers are an increasingly popular way to deploy Linux distros, used by projects such as Bazzite and Blue95.

First, find a bootable container image for the OS you want to run, for instance quay.io/fedora/fedora-bootc. Note that a regular image like docker.io/fedora will not work for this since it’s missing software such as systemd which are needed to fully boot up the OS.

On Hyades, create the microVM as usual but make sure you add the environment variable KRUN_INIT_PID1=1 for your microVM. Now start up the microVM and run exec NAME bash to get a root shell.

Before bootup, a /etc/resolv.conf is generated with the MIT nameservers, but it might get clobbered by systemd-resolved, resulting in broken DNS. If that happens, edit /etc/resolv.conf to use hardcoded MIT nameservers:

nameserver 18.0.70.160
nameserver 18.0.72.3
nameserver 18.0.71.151
options edns0

You may also need to edit the hosts: line in /etc/nsswitch.conf to make DNS resolution stop using systemd-resolved.

You can preinstall software by writing a Dockerfile and building it with the Forgejo CI.

NixOS

NixOS has good support for building layered OCI images. Note that this approach only supports NixOS, not arbitrary Linux distros.

First, add the following package to your flake outputs:

packages.${system}.default = pkgs.dockerTools.streamLayeredImage {
  contents = [ self.nixosConfigurations.HOSTNAME.config.system.build.toplevel ];
  config.Entrypoint = [ "/init" ];
  extraCommands = ''
    rm -f etc
    mkdir etc
  '';
  includeNixDB = true;
};

Then, set boot.isContainer = true; in your NixOS config. You can also import the "${nixpkgs}/nixos/modules/profiles/minimal.nix" module to make the final image slightly smaller.

If you want SSH, enable it on a port other than 22 and make a volume on Hyades mounted at /etc/ssh/keys for the SSH host keys:

services.openssh = {
  # Put host keys in a volume instead
  generateHostKeys = false;
  extraConfig = ''
    HostKey /etc/ssh/keys/ssh_host_rsa_key
    HostKey /etc/ssh/keys/ssh_host_ed25519_key
  '';
};

To get networking inside the microVM, add:

networking = {
  # Disable dhcpcd which makes networking really slow
  dhcpcd.enable = false;
  # Stop NixOS from clobbering krun's /etc/resolv.conf
  resolvconf.enable = false;
};

Now build it with nix build, which will produce a shell script result that assembles the Docker image when run. Load it into Podman (or Docker) with ./result | podman load (do not use podman import) and push it to your favorite OCI image registry.

Finally, on Hyades, set the environment variables KRUN_INIT_PID1=1 and PATH=/run/current-system/sw/bin and try booting it up! Like with bootable containers, you can run exec NAME bash to get a root shell.

Bugs

If you encounter any bugs, please report them on the Hyades bug tracker. The one exception is security vulnerabilities, which should instead be disclosed via email to sipb-hyades-root@mit.edu.


  1. Technically this is false. Hyades uses Podman and krun which runs the microVM inside a container.↩︎