9.1. Creating Morloc environments

An environment is a named, self-contained Morloc installation: the Morloc compiler and runtime, a solved toolchain of language runtimes and packages, and a data directory holding the programs you install. Every deployment starts by making one. This section covers how to create an environment, choose where it lives, work inside it, change it, and remove it.

If you do not have mim yet, install it as described in Installing mim:

$ curl -fsSL https://raw.githubusercontent.com/morloc-project/morloc-manager/main/scripts/install.sh | sh

9.1.1. Native and container environments

mim builds an environment in one of two ways, called backends.

  • Native: the toolchain is installed with pixi (a conda-forge and PyPI package manager) into a private directory under your home, and programs run directly on your host. No container engine is needed. This is the default wherever it works: glibc Linux on x86-64 or ARM, Apple Silicon macOS, and NixOS with nix available.

  • Container: the same toolchain is built into a container image with Docker or Podman, and every command runs in a container started from it. Your current directory is mounted at /work inside it.

Both behave the same for writing, building, and serving programs. The difference that matters for deployment is the last step: only a container environment can be frozen into a portable image (Freezing). Since this chapter ends with a freeze, the running example uses Podman:

$ mim new --engine podman --set-default smiles

--engine takes podman, docker, apptainer (or its older name singularity), or none for the native backend. Apptainer is experimental (see Installing Morloc). Leave --engine off and mim picks the native backend on hosts that support it, or the one container engine it finds installed. If it finds several, it asks you to name one. The first choice you make is remembered for later environments.

The name, smiles here, is optional. Without one, the environment is named after the Morloc version it tracks: latest, or v0.105.2 when you pin a version with --morloc-version 0.105.2.

The first mim new is slow. It downloads the Morloc compiler, solves the toolchain, and, for a container environment, builds an image (named localhost/morloc-env:<name>). Later environments reuse the downloaded compiler, and rebuilding with unchanged requirements skips the solve.

9.1.2. The default environment

Every mim command acts on one environment: the one named with --env, or otherwise the default. The first environment you create becomes the default on its own; --set-default makes any later one the default as it is created. To change it afterwards:

$ mim modify --env smiles --set-default

There is no activation step and no environment variable that selects an environment. Keep the default stable and reach for --env when you need another one, so a bare command in your shell history always means the same thing.

$ mim ls                  # every environment; the default is marked
$ mim info smiles         # backend, version, folders, solved languages
$ mim info smiles --packages   # every package in the solved toolchain
$ mim doctor --env smiles      # health checks; non-zero exit on failure

9.1.3. Local and system environments

An environment lives in one of two scopes:

Scope Configuration Data

local (default)

~/.config/morloc/environments/<name>/

~/.local/share/morloc/environments/<name>/

system (--system)

/etc/morloc/environments/<name>/

/usr/local/share/morloc/environments/<name>/

A local environment belongs to you and needs no privileges. $XDG_CONFIG_HOME and $XDG_DATA_HOME move the two local directories if you set them.

A system environment is shared by every user on the machine. Creating, changing, and removing one requires root, so those commands run under sudo. Using it does not: any user can run and shell in it, and can make it their personal default without sudo.

$ sudo mim new --system --engine podman shared
$ mim modify --env shared --set-default         # your own default
$ sudo mim modify --env shared --set-default --system   # everyone's default

When a local and a system environment share a name, --env finds the local one. mim info <name> --system describes the system one.

System environments and container images

Docker keeps one image store for the whole machine, so a system environment built with Docker is visible to every user. Podman keeps a store per user, so an image built by root under sudo is invisible to rootless Podman until you add root’s store as an extra, read-only one. Add this line to the [storage.options] section of /etc/containers/storage.conf:

additionalimagestores = ["/var/lib/containers/storage"]

On some distributions, Fedora and Debian among them, a shared store can cause storage locking conflicts, which is why mim suggests Docker for system environments.

9.1.4. Working inside an environment

mim run executes one command in an environment, and mim shell opens an interactive shell in it. Everything after -- is the command:

$ mim run -- morloc --version
$ mim run --env shared -- morloc --version
$ mim shell

Inside, morloc, the language runtimes, and every program installed in the environment are on the PATH. Your current directory is the working directory: mounted at /work in a container, used as-is on the native backend. A shell’s prompt is tagged with the environment’s name, and exit leaves it.

A container does not see your host’s environment variables. Pass the ones a command needs with --env-var, or a file of them with --env-file:

$ mim run --env-var OMP_NUM_THREADS=4 -- printenv OMP_NUM_THREADS   # set a value
$ mim run --env-var http_proxy -- printenv http_proxy              # copy the host's value

On a host with SELinux enforcing (Fedora, RHEL), mim relabels the mounted directory with the :z suffix so the container may read it. Relabeling some directories would be unsafe, so mim refuses to run from /, or from /tmp or /var/tmp and anything below them. Run from your home directory itself and it skips the working-directory mount with a warning. Work in a project directory such as ~/smiles-project.

9.1.5. Changing an environment

mim modify changes an environment’s settings. Some changes take effect at once; others alter the toolchain and rebuild the environment at its current Morloc version. A rebuild that fails rolls the settings back, so a typo never leaves the environment broken.

Flag Effect

--set-default, --unset-default

Change the default. No rebuild.

--env-name <new>

Rename (container environments only). No rebuild.

--dotfiles <dir>

Copy a directory into the container’s home directory. No rebuild.

--lang py,r@4.3

Keep these language toolchains installed, optionally pinned. Futhark is Docker and Podman only. Rebuilds.

--conda-packages-file <file>

Extra conda-forge packages, one per line, such as jq or samtools. Rebuilds.

--system-packages-file <file>

Extra apt packages, container environments only. Rebuilds.

--base heavy|light

The container’s base image: heavy is ubuntu:24.04 (the default), light is the smaller debian:bookworm-slim. Rebuilds.

--flagfile <file>

Extra container engine flags (below). No rebuild; build flags apply at the next rebuild.

Each flag that sets something has a --no- form that clears it, such as --no-conda-packages-file. A package file is the whole list for its source: to add one package, edit the file and pass it again.

mim update rebuilds the environment. Without flags it keeps the Morloc version it has; it never moves the version by surprise.

$ mim update --env smiles                         # rebuild, same version
$ mim update --env smiles --latest                # move to the newest release
$ mim update --env smiles --morloc-version 0.105.2
$ mim update --env smiles --reinit                # discard the solve and start over

Moving the Morloc version does not recompile the programs already installed; reinstall them afterwards (Dependency management).

9.1.6. Extra container flags

Anything a container engine needs beyond what mim passes on its own — a GPU device, a proxy, a bind mount, a hostname — goes in an engine flag file. It is a YAML document divided by phase, the engine command an environment runs: build (the image build, at new, update, a modify that rebuilds, and freeze), run (run, shell, install), and start (the serving container). Under each phase, an engine section (docker, podman, apptainer) lists the flags that engine gets, and all lists flags every engine gets:

flags.yaml
build:
  all:
    - --build-arg=HTTP_PROXY=http://proxy.example.com:3128
  podman:
    - --no-cache

run:
  all:
    - --volume=/data/reference:/ref:ro     # a host directory every program can read
  apptainer:
    - --nv                                 # NVIDIA GPU passthrough
  podman:
    - --device=/dev/dri

start:
  all:
    - --hostname=morloc-serve

Every section is optional. The schema is strict: an unknown phase or engine name, such as a misspelt podmann:, is an error rather than ignored. Each entry passes through the shell before it reaches the engine, so $HOME, ~, and globs expand, an unquoted space splits an entry into two arguments, and a value containing a space must be quoted as it would be in a shell.

Install the file when creating the environment, or later with modify:

$ mim new gpu --engine podman --flagfile flags.yaml
$ mim modify --env gpu --flagfile flags.yaml   # replace it
$ mim modify --env gpu --no-flagfile           # remove it

The file is validated and copied whole, comments included, to the environment’s configuration directory as env.flags.yaml, replacing any previous one. mim info <name> names it and shows the flags each phase gets for the environment’s engine. The build section is part of what decides whether the image is up to date, so changing it makes the next update rebuild.

For each command the flags are <phase>.all followed by <phase>.<engine>, then any one-off flags from the command line. Two command-line overrides exist, and neither changes the stored file:

  • -x <flag> (--engine-arg) adds one flag for this command and can be repeated. On run, shell, and install it joins the run phase, on start the start phase, and on new and update the build phase; update -x always rebuilds.

  • --flagfile <file> on run, shell, install, and start uses that file instead of the stored one, for this command only. This is how a second server for the same environment comes up with different flags.

$ mim run -x --device=/dev/dri -- ./prog render     # one extra flag, this run only
$ mim start --flagfile alt.yaml -p 9090:9090        # a second server, other flags

--platform is refused in the file and on the command line, because the environment’s architecture (mim new --arch) decides it and the toolchain was solved for that architecture. The native backend has no engine to pass flags to, so --flagfile and -x are errors there.

9.1.7. Removing environments

mim rm removes environments by name. For a container environment it also stops its server, removes its image, and removes the volume that held its toolchain. If you remove the default, the default is cleared and mim tells you how to set a new one.

$ mim rm smiles
$ sudo mim rm shared --system

mim nuke removes every environment in a scope after asking for confirmation. Pass --yes to skip the question in a script, --system for the system scope, and --images to also delete the base images the environments were built from. Those base images, such as ubuntu:24.04, may be used by other things on your machine.

Neither command touches the Morloc compilers mim has downloaded, its copy of pixi, or the shared package cache, so a later mim new does not start from nothing.