Deploy to a machine
Put a stream on a server or a small box by the door, keep it running without anyone logged in, update it, and manage it remotely.
There’s no separate deployment system in Tatolab. A machine runs its own runtime, and a stream you keep with tatolab run -d stays running and comes back after every restart. Deploying a stream means getting the project onto the machine and keeping it there.
This page assumes a Linux machine you reach over ssh: a server, a small box next to a camera, or a cloud machine with a GPU.
1. Install the runtime
Section titled “1. Install the runtime”On the machine:
curl -fsSL https://get.tatolab.com/install.sh | shWhen the installer offers to turn on lingering, say yes. Without it, the runtime runs only while someone is logged in; with it, the runtime starts at boot. You can also turn it on later with loginctl enable-linger.
Check it:
tatolab streamsSee Install on Linux for requirements and details.
2. Copy the project
Section titled “2. Copy the project”A project is just files, so use what you already use: git clone, rsync, scp. Leave .venv/ out; it belongs to the machine it was made on.
git clone https://github.com/your-org/door-camera.gitcd door-camerauv syncuv sync builds the project’s .venv on this machine, from your pyproject.toml and uv.lock. Commit uv.lock so the machine gets exactly the versions you tested with.
If a node needs files that aren’t in the repository, such as model weights, put them in place now, so the node doesn’t download them while it starts.
3. Keep the stream
Section titled “3. Keep the stream”tatolab run -ddoor_camera kept (project /home/ops/door-camera)That’s the deployment. The runtime loads the stream now and every time it starts again. To check it survives a reboot, reboot and run tatolab streams.
Machines without a screen
Section titled “Machines without a screen”A server has no desktop, so plan the stream for that:
- Leave out
DisplayWindow. With no display, there’s nowhere for a window to open. - See frames with
exchangeinstead. For any output something reads,tatolab exchange --channel door_camera/camera/video --out snapshots/saves full-resolution PNGs you can copy off the machine. - Expose outputs for other streams and agents on the machine, or record with
Mp4Sink. A kept stream starts its recording over each time it reloads, after every reboot, so record from a separate stream, or copy files off; see Recordings and restarts. - Audio: with no sound devices,
MicrophoneSourceproduces silence andSpeakerSinkdiscards its input, so the stream still runs.
Choose the right camera
Section titled “Choose the right camera”A machine with several cameras needs to say which one. On Linux, list them with v4l2-ctl --list-devices (from the v4l-utils package), then set device_id:
camera = stream_builder.add(CameraSource, config={"device_id": "/dev/video2", "max_width": 1280, "max_height": 720})A device_id that can’t be opened is refused by name when the stream starts, instead of silently falling back to another camera. /dev/video numbers can change when cameras are plugged in a different order; the stable names under /dev/v4l/by-id/ don’t.
Update
Section titled “Update”cd door-cameragit pulluv synctatolab run -dRunning tatolab run -d again for the same project and stream replaces the kept record with the new version.
Manage it from your laptop
Section titled “Manage it from your laptop”Every tatolab command works over ssh. A command run over ssh doesn’t get ~/.local/bin on its PATH on Debian and Ubuntu, so give the path where the installer put tatolab, relative to your home folder there:
ssh door-box .local/bin/tatolab streamsssh door-box .local/bin/tatolab logs --stream door_camera --level warn -fssh door-box .local/bin/tatolab stop door_cameraTo let an agent manage the machine, run tatolab mcp over ssh:
claude mcp add door-box -- ssh door-box .local/bin/tatolab mcpControl never travels over the network by itself: the runtime only listens on a local socket, and remote access is always ssh, with ssh’s keys and permissions.
Tailscale
Section titled “Tailscale”If your machines are on a Tailscale tailnet, ssh to them by their tailnet names, from anywhere, without opening ports. The runtime also uses the machine’s tailnet name as its own name, the first part of every address. Tailscale isn’t required; without it, the machine is named after its hostname.
Containers
Section titled “Containers”Where there’s no systemd, as in most containers, run tatolabd as the container’s main process. It runs in the foreground until it gets a stop signal. Giving the container the GPU and camera devices is your container runtime’s job (for NVIDIA GPUs, the NVIDIA Container Toolkit). A container that shares the host’s network counts as the same machine as the host, so only one of them can run a runtime.
Keep an eye on it
Section titled “Keep an eye on it”tatolab streamsshows every stream’s state. A kept stream that keeps crashing the runtime is markedfailed, with the reason, and the runtime starts the others without it.tatolab logs --stream <name> --level warnshows a stream’s problems;journalctl --user -u tatolabdshows the runtime’s own output.tatolab graphshows whether frames are being dropped, and where.
Several machines
Section titled “Several machines”Each machine runs its own runtime and its own streams. You deploy to each the same way. Streams on one machine read each other’s outputs, and streams on your other machines read public ones by URL, through the runtime’s MoQ relay. See Across machines.
Checklist
Section titled “Checklist”- Runtime installed, lingering on,
tatolab streamsanswers - Project copied,
uv syncrun, model weights and other files in place - No
DisplayWindowon a machine without a screen - Cameras chosen by stable
device_id, recording paths absolute, and recordings safe from a reload -
tatolab run -d, then a reboot, thentatolab streamsshows itkeptand running