π DeploymentΒΆ
TINO ships as a single Docker image with no external dependencies β just point it at an OIDC provider and add a data volume, and youβre up.
π₯οΈ System requirementsΒΆ
TINO is distributed as a multi-architecture Docker image and runs comfortably on modest hardware.
The official ghcr.io/confirm/tino image is published for the following platforms:
linux/amd64β 64-bit x86 (Intel/AMD)linux/arm64β 64-bit ARM (AArch64, including Apple Silicon)
No other architectures are supported.
TINO needs at least 128 MB of memory, but 256 MB is recommended. Typst compilation and active collaboration rooms add to that, so allow more headroom for large documents or many concurrent editors.
Note
32-bit ARM (linux/arm/v7) and RISC-V (linux/riscv64) are not supported.
Several of TINOβs compiled Python dependencies publish no prebuilt wheels for these platforms, and the runtime image intentionally ships without a build toolchain:
linux/riscv64βpycrdt(the Rust library powering real-time collaboration) has no RISC-V wheel.linux/arm/v7βuvloopandhttptools(Uvicornβs accelerated event loop and HTTP parser) have no 32-bit ARM wheels.
β‘ Quick start (demo)ΒΆ
To try TINO without setting up an OIDC provider, disable authentication:
docker run -d \
--name tino \
-e TINO_AUTH_DISABLED=true \
-p 5000:5000 \
-v data:/data \
ghcr.io/confirm/tino
See also
See Disabling authentication for more information.
π³ DockerΒΆ
Docker imageΒΆ
To deploy TINO, use the following Docker image:
ghcr.io/confirm/tino
See also
Check the Git tags for explicit Docker image versions.
Docker commandΒΆ
To deploy TINO via a simple docker command, use the following CLI arguments:
docker run -d \
--name tino \
-e TINO_BASE_URL=https://tino.example.com \
-e TINO_OIDC_DISCOVERY_URL=https://sso.example.com/.well-known/openid-configuration \
-e TINO_OIDC_CLIENT_SECRET=change-me \
-p 5000:5000 \
-v data:/data \
ghcr.io/confirm/tino
Hint
Itβs recommended to deploy TINO via Docker Compose and use the additional deployment settings mentioned in the Compose file.
Docker ComposeΒΆ
Use the following docker-compose.yml Compose file to start TINO:
---
#
# Recommended Docker Compose deployment for TINO.
#
# This file is verbose and heavily commented by design. TINO would run with
# fewer options, but that is not recommended.
#
# Copy this file, adjust the values below, then run:
#
# docker compose up -d
#
services:
tino:
#
# π³ Docker image
#
# Pin a release tag (e.g. ghcr.io/confirm/tino:1.2.3) for reproducible deployments.
#
# Find tags right here:
# https://github.com/confirm/tino/pkgs/container/tino
#
image: ghcr.io/confirm/tino
#
# π Environment
#
# TINO is configured via environment variables.
#
# See the configuration reference for the full list of TINO_* environment variables:
# https://docs.tinotype.com/operations/configuration.html
#
environment:
# Public URL TINO is served on (used for OIDC redirects and, if enabled, MCP).
TINO_BASE_URL: https://tino.example.com
# Your OIDC provider's discovery endpoint.
TINO_OIDC_DISCOVERY_URL: https://sso.example.com/.well-known/openid-configuration
# OIDC client secret β replace this.
#
# β οΈ Keep secrets out of version control, inject it instead. E.g.:
#
# - Via an .env file
# - Via a secrets manager
# - Via CI variables
#
TINO_OIDC_CLIENT_SECRET: change-me
#
# π Network
#
# TINO listens on port 5000 inside the container, which is mapped from the
# host to the container.
#
# β οΈ Typically a TLS-terminating reverse proxy sits in front of it.
#
# When it does, do one of the following:
#
# - Either bind to localhost ('127.0.0.1:5000:5000'),
# - or drop the port mapping and share an internal network.
#
ports:
- '5000:5000'
#
# π Deployment
#
# The deployment resource constraints are optional, but it's recommended as
# they limit the resources TINO can use.
#
# Feel free to adapt them to your requirements.
#
# For the system requriements, check out:
# https://docs.tinotype.com/operations/deployment.html#system-requirements
#
deploy:
resources:
limits: # hard ceiling enforced by the runtime
cpus: '1'
memory: '256M'
reservations: # minimum guaranteed for scheduling
cpus: '0.1'
memory: '128M'
#
# π Security
#
# TINO runs without defining the security-related options below.
#
# However, it's recommended to use them, as they harden the container,
# resp. runtime environemnt in case anything goes wrong.
#
# Prevent the process and its children from gaining new privileges
# (e.g. via setuid binaries).
security_opt:
- no-new-privileges:true
# Drop all Linux capabilities β TINO runs as a non-root user and binds a
# high port (5000), so it needs none of them.
cap_drop:
- ALL
# Run with a read-only root filesystem...
read_only: true
#
# π Filesystem
#
# TINO requires 2 writable directories:
#
# 1. The data directory for the persisted user data (buckets, fonts and local packages).
# 2. The tmp directory (Typst cache, Gunicorn worker files).
#
# Add tmpfs (required when `read_only: true` is set).
tmpfs:
- /tmp
# Persisten data volume.
volumes:
- data:/data
#
# π Restart
#
# Restart the container automatically unless it was explicitly stopped.
restart: unless-stopped
#
# πΎ Volume
#
# Named volume backing TINO_DATA_DIR (mounted at /data in the container).
#
# β οΈ Don't forget to backup this volume!
#
volumes:
data:
Then bring the stack up with:
docker compose up -d
π¦ Standalone (without Docker)ΒΆ
Warning
Running TINO without Docker is not recommended and unsupported. You are responsible for managing Python, the Typst CLI, process supervision, and upgrades yourself. The Docker image is the only officially supported deployment method.
If you cannot use Docker, TINO can be installed as a regular Python package and run with Gunicorn.
PrerequisitesΒΆ
Building the wheelΒΆ
make package
This produces a wheel in build/ (e.g. build/tino-<version>-py3-none-any.whl).
InstallingΒΆ
pip install build/tino-*.whl
This installs TINO and all its Python dependencies (including Gunicorn and Uvicorn).
RunningΒΆ
export TINO_BASE_URL=https://tino.example.com
export TINO_OIDC_DISCOVERY_URL=https://sso.example.com/.well-known/openid-configuration
export TINO_OIDC_CLIENT_SECRET=change-me
# β¦ set any other TINO_* variables (see Configuration)
gunicorn \
-k uvicorn.workers.UvicornWorker \
-w 1 \
-b 0.0.0.0:5000 \
'tino:create_app()'
This is the same command the Docker image runs internally.
Hint
See π Configuration for the full list of environment variables.
Make sure TINO_DATA_DIR points to a
writable directory that is backed up regularly.
π IntegrationΒΆ
Reverse proxyΒΆ
TINO is typically deployed behind a reverse proxy (e.g. nginx, Traefik, Caddy) that terminates TLS.
Set TINO_BASE_URL to the public https:// URL the proxy serves TINO on.
It is used to build OIDC redirect URLs and, when MCP is enabled, as the resource identifier in the OAuth discovery metadata.
OIDCΒΆ
TINO requires an OAuth 2.0 / OpenID Connect (OIDC) provider for authentication. Any provider that supports OpenID Connect Discovery is supported (e.g. Keycloak, Authentik, Azure AD, Okta, Zitadel).
Register a new client (application) with your OIDC provider:
Set the client ID to
TINO_OIDC_CLIENT_ID, or vice-versaSet the redirect URI (callback URL) to:
https://tino.example.com/oidc/callback
Set the post-logout redirect URI to:
https://tino.example.com/login
Ensure the following scopes are enabled:
openid,email,profile.Make sure the ID token includes a claim with the userβs group memberships (see
TINO_OIDC_GROUPS_CLAIM).Make sure a user matches an admin group (see
TINO_ADMIN_GROUPS)Set the
TINO_OIDC_CLIENT_SECRETto the client secret
See also
See π Configuration for the full list of environment variables.
MCPΒΆ
The MCP server is disabled by default.
To enable it, set TINO_MCP_ENABLED to true.
MCP authentication happens via OAuth 2.0, and the OIDC provider must support CIMD. No additional client registration is needed.
Note
On Keycloak this requires version 26.6.0 or later, where Client ID Metadata Document support ships as an experimental feature. Check out the Client Registration docs for more information.
See also
Check out the MCP authentication for how the auth flow works, and the MCP server documentation for usage and settings.
Disabling authenticationΒΆ
For local development, demo environments or external authentication, you can disable the built-in OIDC authentication entirely by setting the TINO_AUTH_DISABLED environment variable:
TINO_AUTH_DISABLED=true
Warning
When authentication is disabled, all requests are treated as a built-in no-auth user with full administrator privileges. OIDC configuration is not required in this mode.
In production this is only safe when TINO sits behind a reverse proxy or gateway that already handles authentication (e.g. OAuth2 Proxy, Authelia, or a cloud IAP).