Ultralab

Manual · Chapter 4

Apps and the Sandbox

This is the page you will spend most of your time on. It walks four steps in the order they genuinely have to happen, and it never lets a ticked box read as a running service.

1 · The runtime #

Nothing else on this page works until a container runtime answers. Press Check and Ultralab asks the machine what it has: Docker or Podman, plus the compose plugin.

If it is missing and you are on a Debian, Ubuntu, Fedora, RHEL or Arch host, Write and run the install writes your distribution's own documented package steps to compose/install-docker.sh and runs them — as root if Ultralab already is, otherwise handing you a single sudo bash … line.

Nothing is piped into a shell unread. The steps go to a file first, on purpose. curl … | sh is the normal way this is done and it means running code you have never seen, fetched over a connection you are trusting, as root. Open the file.

On Windows, this step will tell you it cannot start containers here. That is expected — see chapter 2. Everything else on the page still works; the difference is that Install hands you a command instead of running it.

2 · Finding something #

The catalogue is arranged two ways at once, because there are two questions.

Aisles — reasons to want one

AisleWhat is in it
Where households start Ten applications that between them cover most of what people were paying for. Start here if you do not know what you want.
Stop paying for it Everything that stands in for a subscription. Each card names which one.
Light on the host A quarter of a gigabyte resident or less. The answer to "my machine is small". Once you have measured your lab, this aisle uses the real numbers.
Safe to try now The Sandbox will take these as they stand, and they need nothing from you first.
Asks something of you A value only you have, a device on the host, or a warning worth reading. These two aisles between them contain everything — nothing hides between them.

Layers — where a thing sits in the stack

Media, Photos, Downloads, Files, Identity, Mail, Network, VPN, Edge, Security, Home, Monitoring, Management, Dashboard, Productivity, Development, Backup. Use these when you already know what you are looking for.

Search the thing you pay for

The search box reads the offer as well as the name. Type dropbox and it finds Nextcloud, Syncthing and Filebrowser. Type spotify and it finds Navidrome. 1password finds Vaultwarden; google photos finds Immich. You are not expected to know the project names — that is the whole point.

Reading a card

Each card carries, in order:

  • The name.
  • Instead of … — the subscription it stands in for, where there honestly is one. This is not a claim of feature parity. Immich is not Google Photos; it is where Google Photos sat in your life.
  • Layer, licence, port, container count.
  • A memory badge — ≈1.8 GB estimated until you have measured this machine, then 1.9 GB measured here. Those are different claims and the badge never blurs them.
  • What it does, in a sentence.
  • Usually run with … — the applications it is normally run beside. Named, never ticked. That is your next decision, not Ultralab's.
  • Any warning, and any reason the Sandbox will refuse it.

3 · What will this do? #

The most useful button on the page, and the one people skip. It reads out the install before you press it, for the plane it would actually land in. Nothing is chosen, written or started by asking — looking is free.

It is in five parts, in order of how much they should worry you:

  1. It reaches past the plane. Privileged, a host device, the host network, a kernel capability. These defeat containment and are said as plainly as they can be: "that is root on this host, not root in a container." If this section is there at all, read it. If it is not, that is meaningful too.
  2. Its own warning. The application's caution — "do not open this to the internet", "recording eats disk fast". Advice about running it, kept separate from the section above on purpose: if every cautioned app looked like a containment failure, you would stop reading both.
  3. It starts. Every container, by image, pinned. Including the companions — the database Immich cannot boot without — and why each one is there.
  4. It mounts these Datasets. The folders it will see, and whether it can write them. This is the line that matters. Jellyfin can add, change and delete in media/Movies; it can only read incoming. Plex is read-only against the whole shelf. That difference is a reason to pick one over the other.
  5. It publishes / it will ask you for / and what it cannot do. Ports, on your network only. Values you will have to supply, by name. And the reassurances — which are printed only because something actually enforces them.

"And what it cannot do" is not marketing. Each line there is enforced somewhere in the code: installing an app never changes the edge policy; a rendered mount that leaves the plane is refused before compose sees it; uninstall cannot remove a Dataset. If a reassurance were not enforced it would not be printed.

4 · The Sandbox #

The Sandbox is the reason it is safe to be curious. It is not a flag or a warning label — it is a second, complete world.

LiveSandbox
Compose projectultralabultralab-sandbox
Data root<data root>/<data root>/sandbox/
Your filesMountedNot mounted at all
Ports809618096 — everything shifted by 10000
NetworkIts own bridgeA different bridge
StorageIts own volumesDifferent volumes
LimitsNone imposedMemory and CPU ceiling
DatabaseThe lab'sIts own, separate

A trial cannot damage your photographs because there is no path to them in the file it runs from. This is not a permission check that could be wrong; the folder is simply not there.

What the Sandbox refuses

Some applications cannot be contained by a port offset and a separate folder, and Ultralab says so rather than pretending:

  • Home Assistant wants the host's network stack. There is no port offset that isolates that.
  • Zigbee2MQTT and Frigate want real host devices — a USB radio, a camera. A sandbox that hands those through is not one.
  • Anything privileged or asking for kernel capabilities. Those are granted against the host, not against the plane.

Each says why on its own card. Install those Live, deliberately, when you mean it.

Lending the Sandbox your library

Trying a media application against three test files proves nothing. So there is one deliberate exception: an admin can lend the Sandbox the household library. media/ and photos/ then mount into the trial from the real root, and always read-only. It can read every film you own and rewrite none of them.

Off by default, admin-only, and the only exception the guard grants. A writable lend, any other Dataset, or the Live plane asking to borrow are all still refused.

Promote, or empty it

  • Promote to Live installs the application properly against your real Datasets and destroys the trial.
  • Empty it removes the whole sandbox project, its volumes and its directory. Live is untouched.

The trial's configuration does not come with it. Promote starts fresh against the real Datasets. That is deliberate: a configuration built against fake data is not one your household should inherit. Set it up properly once it is Live.

5 · Installing for real #

Install brings up that application's services and leaves everything else running. One app at a time, on purpose — adding a container should not restart your media server.

Ultralab will refuse before Docker does, where it can:

  • Port clashes. Installing Pi-hole while AdGuard Home holds port 53 is refused with both names and the port.
  • Its own port. Nothing may publish the port Ultralab's household page is serving on.
  • Missing values. An application that needs something only you have will not start until you have supplied it.

On Windows, Install renders the compose file and shows you the command. Copy it, run it on the Linux host.

6 · Values and secrets #

Two kinds of setting, treated completely differently:

GeneratedYours
Examples Database passwords, admin tokens A VPN provider's key, a Cloudflare tunnel token
Who makes itUltralab, at randomOnly you have it
You type itNeverOnce, by name, before it will start
In a BuildNeverNever — only its name

All of them live in compose/.env, which is kept private and is never written into the YAML. The compose file carries ${ULTRALAB_IMMICH_DB}; the file beside it carries the value. That is why a rendered compose file is safe to read out to somebody and the .env is not.

7 · Pins and updates #

Every image in the catalogue is pinned. :latest is never a household contract — it means "whatever the vendor pushed this morning", which is how a working lab breaks overnight.

Where a vendor publishes no stable version tag, Verify pins resolves their channel to a sha256: digest on your lab host, and every render afterwards carries the digest. A tag is how the pin was found; the digest is the pin.

Moving forward on purpose

Pinning without a way forward is how a lab goes quietly two years stale, so Versions is the other half. Name a released version; Ultralab re-resolves the digest, re-renders and re-applies, keeping the previous render. If it goes badly, Roll back puts the old one back. That safety net is the whole reason offering an update at all is reasonable.

Check for rebuilt images tells you when a digest you are pinned to has been rebuilt under the same tag upstream — usually a security fix.

Watchtower is in the catalogue and you probably should not use it. It auto-updates containers, which undoes every pin above. It is listed because you may have a reason; its card says the same thing.

8 · Bringing your own app #

The catalogue is 56 applications. A household lab is whatever the household runs. So the Apps page has a Bring your own app panel: paste a compose file, Ultralab checks it against the plane, and keeps it if it passes.

How to do it

  1. Name it. That becomes compose/custom/<name>.yml.
  2. Choose the plane. Sandbox is ticked by default. Leave it ticked the first time — a compose file you found on the internet is exactly what the Sandbox is for.
  3. Start from the example. The button fills the box with a file that already passes, carrying your lab's paths. The commonest reason a first attempt is refused is a path copied from somebody else's machine, so the starting point has yours in it.
  4. Check and keep. Ultralab reads it and either keeps it or tells you exactly what is wrong.
  5. Bring it up. Ultralab keeps the file; starting it is still docker compose, and still yours. The verdict box gives you the command.

What gets refused, and why

RefusedWhy
A bind mount outside the plane's data root The entire boundary. /etc:/host-etc is not a container, it is a filesystem browser.
A host deviceCannot be given back once granted.
An added capabilityGranted against the host kernel, not the plane.
privileged: trueRoot on the host.
A shared host namespaceSame reasoning.
Anything relaxing the container's confinementSame reasoning.
A file it cannot read confidently The important one. Ultralab scans lines rather than parsing YAML, and some valid compose is invisible to a line scanner — so anything ambiguous is refused rather than approved. A guard that does not understand a file has not checked it.

Named volumes are fine. Ports are fine. The example gives you the shape:

services:
  my-app:
    image: alpine:3.20
    volumes:
      - "<your data root>/share:/data"    # a Dataset, by its real path
      - "my-app-state:/state"              # its own state, a named volume
    ports:
      - "8100:8080"
    restart: unless-stopped
volumes:
  my-app-state:

Your own compose travels with a Build. When you save a setup, custom files come with it — with your machine's paths swapped for a token, and the importing lab's paths swapped back in before its own guard sees the file. See chapter 5.

9 · Getting rid of one #

Uninstall removes that application's containers. By default it keeps its own configuration and database volumes, so reinstalling resumes where you left off. Tick the purge box to remove those too.

Either way, your Datasets are never touched. Uninstalling Jellyfin does not remove a single film. There is no combination of buttons on that page that deletes your media, which is deliberate.


← Chapter 3 · Chapter 5 · Builds →