plym Docs
    plym.io

    Basics

    plym is highly configurable on both the backend and the templating side. Learn how to customise your plym instance or template.

    Updated Aug 04, 2026

    On this page

    plym reads its settings from two YAML files: config.yaml, which you own, and template.yaml, which ships inside the active template.

    config.yaml sits at the root of your blog directory and holds every operator setting — identity, branding, caching, HTML injection, pagination.

    template.yaml sits at templates/<name>/template.yaml and holds the design defaults the template author picked: fonts, colors, and the Prism theme.

    plym merges the two at startup, key by key, and your config.yaml wins every collision. Unlike .env, neither file holds credentials — no database password, JWT secret, or admin login belongs here.

    Precedence

    template.yaml is merged under config.yaml. The merge is per key, not per block, so setting one color in config.yaml keeps the template's other three.

    The docs template declares four colors:

    colors:
      primary:    "#1C1712"
      secondary:  "#6B6055"
      accent:     "#D2340A"
      background: "#FFFCF6"

    Your config.yaml overrides one of them:

    template: docs
    colors:
      accent: "#00FF00"

    The effective values:

    Key Value Source
    colors.primary #1C1712 template.yaml
    colors.secondary #6B6055 template.yaml
    colors.accent #00FF00 config.yaml
    colors.background #FFFCF6 template.yaml

    template.yaml accepts three keys

    fonts, colors, and prism.theme. Any other key — including prism.enabled and prism.languages, which are yours alone — fails validation and the api does not start.

    Configuration is file-only

    Edit the file, then apply the change with the CLI.

    GET /api/config reads back the merged result, which is the fastest way to see which file won:

    curl http://localhost:9173/api/config \
      -H "Authorization: Bearer $PLYM_TOKEN"
    {
      "name": "plym",
      "description": null,
      "website": "plym.io",
      "blog_home": "plym.io/blog",
      "blog_prefix": "/blog",
      "language": "en",
      "template": "docs",
      "fonts": {"heading": "Space Grotesk", "body": "Archivo"},
      "colors": {"primary": "#1C1712", "secondary": "#6B6055", "accent": "#00FF00", "background": "#FFFCF6"},
      "prism": {"enabled": true, "languages": "python", "theme": "tomorrow"},
      "logo": "/blog/static/logo-f5924e8c.webp",
      "favicon": "/blog/static/favicon-8b458c56.ico"
    }

    logo and favicon come back as local paths because plym downloads both at startup and serves them from its own origin.

    Changes apply on restart

    The api reads both files once, at startup, and caches the result. Nothing is re-read per request, so every edit needs plym reload or plym rebuild. See Instance configuration for which command to run.

    A rejected value stops the api

    Validation runs at import time. A bad value — a string where an integer belongs, an inject snippet containing </body> — raises a ValidationError and the process exits.

    Published posts keep serving through it, because they are static files on disk. The blog index returns 502 until you fix the file and restart, since the index is rendered by the api on request. Check docker compose logs api for the failing key.