Example Server Configuration#

config.example.yml#
# =============================================================================
# Tiled Server Configuration
# config.yml
# =============================================================================
# Start the server with:
#   tiled serve config config.yml
#
# For secrets, prefer environment variables over hardcoding values here.
# Generate a secure random key with:
#   openssl rand -hex 32
#   python -c "import secrets; print(secrets.token_hex(32))"
# =============================================================================


# -----------------------------------------------------------------------------
# Catalog
# -----------------------------------------------------------------------------
# The catalog is the primary data store for a Tiled server. It consists of:
#   - A SQL database that stores metadata and URIs pointing to data assets
#   - Writable storage locations where data assets are written
#   - Readable storage locations where externally-managed data can be read
#
# It is possible for a Tiled server to serve data from *multiple* catalogs.
# This approach is not generally recommended; a single catalog database
# is recommended even for large-scale deployments. See the section
# on Trees, at the end of this file, for more on this advanced topic.
# Every configuration must include either catalog: or trees:, but not both.
#
# catalog:
#   # SQLAlchemy connection URL for the metadata database.
#   # PostgreSQL: postgresql://user:password@host:5432/tiled_catalog
#   # SQLite:     sqlite:////absolute/path/to/catalog.db
#   uri: ${TILED_CATALOG_URI}
#
#   # Locations where Tiled will write data assets.
#   # Each entry is either a directory path (for file-based storage), a URI,
#   # or an object describing S3-style storage.
#   # When an Adapter for a given format supports multiple storage
#   # types (e.g., files or blobs) the first in this list gets priority.
#   writable_storage:
#     - "/storage/data"                  # file-based storage
#     - "duckdb:///storage/data.db"      # embedded tabular storage
#     # S3-style blob a.k.a. bucket storage
#     - provider: s3
#       uri: "http://localhost:9000"
#       config:
#         access_key_id: ${BUCKET_ACCESS_KEY_ID}
#         secret_access_key: ${BUCKET_SECRET_ACCESS_KEY}
#         bucket: "my_bucket"
#         virtual_hosted_style_request: false
#         client_options: {"allow_http": true}
#
#   # Read-only storage for externally-managed data that Tiled serves but
#   # does not write. Accepts the same storage types as writable_storage above.
#   # Note: writable_storage is automatically readable; no need to repeat it here.
#   readable_storage:
#     - "/storage/data"                  # file-based storage
#     - "duckdb:///storage/data.db"      # embedded tabular storage
#     # S3-style blob a.k.a. bucket storage
#     - provider: s3
#       uri: "http://localhost:9000"
#       config:
#         access_key_id: ${BUCKET_ACCESS_KEY_ID}
#         secret_access_key: ${BUCKET_SECRET_ACCESS_KEY}
#         bucket: "my_bucket"
#         virtual_hosted_style_request: false
#         client_options: {"allow_http": true}
#
#   # Create the metadata database automatically on first startup if it does
#   # not already exist. Convenient for single-instance deployments.
#   #
#   # WARNING: In a horizontally-scaled deployment (multiple containers or
#   # workers starting simultaneously), this can cause a race condition where
#   # multiple processes attempt to initialize the database at the same time.
#   # If that is a concern, set this to false and initialize the database once
#   # manually before starting the server:
#   #
#   #   tiled catalog init URI
#   #
#   init_if_not_exists: true
#
#   # Metadata the root node of the catalog
#   metadata: {}
#   specs: []
#   top_level_access_blob: null
#
#   # Database connection pool tuning
#   catalog_pool_size: 5
#   storage_pool_size: 5
#   catalog_max_overflow: 10
#   storage_max_overflow: 10


# -----------------------------------------------------------------------------
# Streaming Cache
# -----------------------------------------------------------------------------
# Required only if you are serving live-updating / streaming data sources.
# For static data repositories this section can be omitted entirely.
#
# Two backend options:
#
#   memory — caches recent updates in process memory. Fine for demos and
#            single-process deployments; not horizontally scalable.
#
#   Redis  — recommended for production and multi-worker deployments.
#            Install Redis and provide a connection URI.
#
# streaming_cache:

#   # In-memory backend (single process only):
#   uri: memory

#   # Redis backend:
#   uri: ${TILED_REDIS_URI}        # e.g. redis://:password@localhost:6379
#   data_ttl: 86400                # seconds — retain streamed data chunks
#   seq_ttl: 86400                 # seconds — retain per-stream sequence counter
#   socket_timeout: 86400          # seconds — timeout on established connection
#   socket_connect_timeout: 10     # seconds — timeout on initial connect


# -----------------------------------------------------------------------------
# Authentication
# -----------------------------------------------------------------------------
authentication:

  # ---------------------------------------------------------------------------
  # Identity Providers
  # ---------------------------------------------------------------------------
  # Uncomment exactly one of the following blocks.
  # If no provider is configured, Tiled runs in single-user mode (see
  # single_user_api_key below).

  # OPTION 1 — Local system accounts (PAM)
  # Users log in with their OS username and password (Linux/macOS).
  # Requires:  pip install pamela
  #
  # providers:
  #   - provider: local
  #     authenticator: tiled.authenticators:PAMAuthenticator
  #     args:
  #       service: login         # PAM service name. Default is "login".


  # OPTION 2 — OpenID Connect (ORCID, Google, Azure, …)
  # Tiled never touches users' passwords; the OIDC provider handles that.
  # Requires:  pip install httpx
  #
  # Setup steps:
  #   1. Deploy Tiled behind HTTPS.
  #   2. Register a new OAuth2 application with your chosen OIDC provider.
  #      Add these redirect URIs (replace <BASE_URL> with your public URL):
  #        https://<BASE_URL>/api/v1/auth/provider/<PROVIDER_NAME>/code
  #        https://<BASE_URL>/api/v1/auth/provider/<PROVIDER_NAME>/device_code
  #   3. Copy the Client ID and Client Secret from the provider dashboard.
  #      Store the secret in an environment variable (see client_secret below).
  #   4. Find your provider's well-known endpoint, for example:
  #        Google : https://accounts.google.com/.well-known/openid-configuration
  #        ORCID  : https://orcid.org/.well-known/openid-configuration
  #        Azure  : https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration
  #
  # providers:
  #   - provider: google          # Must match the label used in the redirect URIs above
  #     authenticator: tiled.authenticators:OIDCAuthenticator
  #     args:
  #       audience: tiled         # Checked against the "aud" claim in the token
  #       client_id: YOUR_CLIENT_ID_HERE
  #       client_secret: ${OIDC_CLIENT_SECRET}
  #       well_known_uri: https://accounts.google.com/.well-known/openid-configuration


  # OPTION 3 — Development / demo only: dictionary of hardcoded passwords
  # Not suitable for production. Passwords should still be set via environment
  # variables rather than written directly into this file.
  #
  # Start the server with:
  #   ALICE_PASSWORD=secret1 BOB_PASSWORD=secret2 tiled serve config config.yml
  #
  # providers:
  #   - provider: toy
  #     authenticator: tiled.authenticators:DictionaryAuthenticator
  #     args:
  #       users_to_passwords:
  #         alice: ${ALICE_PASSWORD}
  #         bob:   ${BOB_PASSWORD}


  # OPTION 4 — Development only: accept any username + password (no validation)
  # Useful for smoke-testing access-control policies without real users.
  # Never expose this to a network.
  #
  # providers:
  #   - provider: toy
  #     authenticator: tiled.authenticators:DummyAuthenticator


  # ---------------------------------------------------------------------------
  # Anonymous / Public Access
  # ---------------------------------------------------------------------------
  # This setting is independent of which provider is configured above.
  #
  # false (default) — unauthenticated users cannot see any data, only the
  #                   HTML landing page and related non-sensitive entry points
  # true            — unauthenticated users can see any entries not explicitly
  #                   restricted by an access policy
  #
  # This can be combined with any provider above to create a service that has
  # both public and private content — e.g. a PAM or OIDC server where some
  # datasets are openly readable and others require login.
  # It can also be used with no provider at all, for a fully open public server.
  #
  # allow_anonymous_access: false


  # ---------------------------------------------------------------------------
  # Single-user API key
  # ---------------------------------------------------------------------------
  # Used only when no providers are configured (single-user mode).
  # If unset, a random key is generated and printed to the terminal at startup.
  # Set it explicitly when multiple server instances must share the same secret
  # (e.g. behind a load balancer).
  #
  # single_user_api_key: ${TILED_SINGLE_USER_API_KEY}


  # ---------------------------------------------------------------------------
  # Token Signing Keys
  # ---------------------------------------------------------------------------
  # REQUIRED for any multi-user setup (i.e. whenever providers: is configured).
  # These secrets are used to sign and verify session tokens.
  #
  # Tiled accepts tokens signed with *any* key in the list, but always signs
  # new tokens with the first one. This allows zero-downtime key rotation:
  # add your new key at the top, redeploy, then remove the old key and redeploy
  # again once all old tokens have expired.
  #
  # Generate a key: openssl rand -hex 32
  # Never hardcode secrets here — always use environment variables.
  #
  # secret_keys:
  #   - ${TILED_SECRET_KEY}


  # ---------------------------------------------------------------------------
  # Admin Users
  # ---------------------------------------------------------------------------
  # Grant admin role to specific identities. The provider string must match
  # a provider label defined above. Admins can manage users and sessions via
  # the /api/v1/admin/* endpoints.
  #
  # tiled_admins:
  #   - provider: local
  #     id: alice
  #   - provider: google
  #     id: bob@example.com


  # ---------------------------------------------------------------------------
  # Session Lifetime
  # ---------------------------------------------------------------------------
  # How long an *inactive* session stays alive before the user must re-authenticate.
  # Measured from the last token refresh. Default: 604800 (7 days).
  #
  # refresh_token_max_age: 604800     # seconds (7 days)

  # Hard ceiling on any session, active or not. Useful for compliance requirements
  # e.g. "all users must re-authenticate every 30 days". Default: unlimited.
  #
  # session_max_age: 2592000          # seconds (30 days)

  # Lifetime of the short-lived internal access token. Transparent to users;
  # controls how often the client silently refreshes in the background.
  # Cannot be revoked mid-life, so keep it short. Default: 900.
  #
  # access_token_max_age: 900         # seconds (15 minutes)


# -----------------------------------------------------------------------------
# Authentication Database
# -----------------------------------------------------------------------------
# When authentication providers are configured above, Tiled persists identities,
# sessions, and API keys in a SQL database. This is separate from any catalog
# database used to store scientific data.
#
# If omitted, Tiled falls back to an in-process SQLite database in memory —
# fine for development, not for production.
#
# Tiled officially supports PostgreSQL and SQLite.
# Any SQLAlchemy-compatible engine may work but is not guaranteed.
#
# database:
#   # SQLAlchemy connection URL.
#   # PostgreSQL: postgresql://user:password@host:5432/tiled_auth
#   # SQLite:     sqlite:////absolute/path/to/auth.db
#   uri: ${TILED_DATABASE_URI}
#
#   # Create tables automatically on first startup if the database is empty.
#   # Safe to leave on; it is a no-op once tables exist.
#   init_if_not_exists: true
#
#   # Test connections before use to catch stale sockets early. Recommended.
#   pool_pre_ping: true
#
#   # Number of persistent database connections to keep open. Minimum 2.
#   pool_size: 5
#
#   # Extra connections allowed when the pool is exhausted.
#   max_overflow: 5


# -----------------------------------------------------------------------------
# Access Control
# -----------------------------------------------------------------------------
# Fine-grained, per-entry access control. Requires authentication providers
# to also be configured above.
# If omitted, all authenticated users can read all entries (and anonymous users
# can read all entries when allow_anonymous_access is true).
#
# access_control:
#   access_policy: tiled.access_policies:SimpleAccessPolicy
#   args:
#     provider: local           # Must match a provider label defined above
#     access_lists:
#       alice: "read:data"
#       bob:   "read:metadata"
#     # Allow unauthenticated users to read entries covered by this policy.
#     # Also requires allow_anonymous_access: true under authentication: above.
#     public: false


# -----------------------------------------------------------------------------
# Server / Uvicorn
# -----------------------------------------------------------------------------
# Low-level HTTP server settings. Most deployments behind a reverse proxy
# (nginx, Traefik, …) only need host and port.
#
# uvicorn:
#   host: 127.0.0.1         # Use 0.0.0.0 to accept connections from any interface
#   port: 8000
#   workers: 1              # Number of worker processes. Defaults to $WEB_CONCURRENCY or 1.
#   root_path: ""           # Set if Tiled is mounted at a sub-path behind a reverse proxy
#   proxy_headers: true     # Trust X-Forwarded-* headers from the proxy
#   forwarded_allow_ips: "127.0.0.1"   # or "*" to trust all proxies
#
#   # TLS — only if Tiled itself terminates TLS.
#   # Leave commented out when a reverse proxy handles TLS.
#   ssl_keyfile:  /path/to/key.pem
#   ssl_certfile: /path/to/cert.pem


# -----------------------------------------------------------------------------
# CORS — Cross-Origin Resource Sharing
# -----------------------------------------------------------------------------
# List domains of browser-based apps that need to call this server directly.
# Omit if no cross-origin browser access is needed (CLI / Jupyter-only usage).
#
# allow_origins:
#   - https://my-dashboard.example.com
#   - https://chart-studio.plotly.com


# -----------------------------------------------------------------------------
# Response Limits
# -----------------------------------------------------------------------------
# Cap the maximum size of a single response. The default (300 MB) is
# intentionally conservative — clients should request data in chunks.
#
# response_bytesize_limit: 314572800   # bytes (300 MB)
#
# For containers with many items, computing an exact count is expensive.
# Above this threshold Tiled returns an estimate instead. Default: 100.
#
# exact_count_limit: 100


# -----------------------------------------------------------------------------
# Metrics
# -----------------------------------------------------------------------------
# Prometheus metrics are exposed at /metrics by default.
# Set to false to disable.
#
# metrics:
#   prometheus: true


# -----------------------------------------------------------------------------
# Custom Media Types / Exporters
# -----------------------------------------------------------------------------
# Register additional export formats beyond Tiled's built-ins.
# Map a MIME type to an importable serializer function for each structure type.
#
# media_types:
#   array:
#     application/x-my-format: my_package.exporters:array_exporter
#   dataframe:
#     application/x-my-format: my_package.exporters:dataframe_exporter
#   dataset:
#     application/x-my-format: my_package.exporters:dataset_exporter
#
# Convenience aliases so clients can request ?format=myext
#
# file_extensions:
#   myext: application/x-my-format


# -----------------------------------------------------------------------------
# Trees
# -----------------------------------------------------------------------------
# Every configuration must include either catalog: or trees:, but not both.
# The 'catalog:' section is recommended for most setups, including large
# deployments.
#
# The 'trees:' section is an advanced feature that can be used for:
#
# - Managing the legacy of Tiled's precursor, databroker
# - Experimentation with alternative catalog implementations
# - Advanced routing that uses PostgreSQL read-only replicas for
#   certain requests (experimental, undocumented)
#
# This section:
#
# trees:
#   - path: /
#     tree: catalog
#     args:
#       uri: ${TILED_CATALOG_URI}
#
# is equivalent to:
#
# catalog:
#   uri: ${TILED_CATALOG_URI}
#
# but the 'trees' form enables serving multiple catalogs, mounted
# at different URL path prefixes.
#
# trees:
#   - path: /a
#     tree: catalog
#     args:
#       uri: ${TILED_CATALOG_A_URI}
#   - path: /b
#     tree: catalog
#     args:
#       uri: ${TILED_CATALOG_B_URI}
#
# It also supports custom alternatives to Tiled's catalog:
#
# trees:
#   - path: /
#     tree: databroker.mongo_normalized:MongoAdapter
#     args:
#       uri: ${MONGO_URI}