PullMD

Anything in, clean Markdown out. Web pages, documents and YouTube to Markdown for people and AI agents.

  • ghcr.io/hsinghhira/pullmd:latest
  • v3.16.0
  • Updated 28 Oct 2026
  • port 6132
  • amd64 · arm64
  • AI
  • 508 stars

Screenshots

PullMD screenshot 1: Convert a pagePullMD screenshot 2: Light and dark

Open the app

Web App

http://localhost:6132/

Help and API

http://localhost:6132/help

MCP Server

http://localhost:6132/mcp

About PullMD

PullMD turns web pages, documents, images, audio and YouTube videos into clean Markdown. Use it from the web app, the REST API, the MCP server or a Claude skill. This is PullMD in a single container, with one port and one data folder. The Trafilatura and MarkItDown helpers are inside. The Playwright helper (headless Chromium, about 3.7 GB) is left out, so pages that need JavaScript fall back to plain extraction.

Features

  • Web pages through Cloudflare Markdown, Readability and Trafilatura
  • Reddit threads and Hacker News discussions with their comments
  • PDF, Word, PowerPoint, Excel and EPUB, by link or upload
  • YouTube title, description and transcript with timecodes, no API key
  • Image captions and audio transcripts with any OpenAI-compatible model (optional)
  • Only the parts that match a question (?query=), to save tokens
  • Share links that keep the last good copy if the page goes away
  • MCP server for AI assistants, and optional sign-in with API keys

How to use

  1. Open PullMD from your dashboard.
  2. Paste a link, or drop a file, and press Convert.
  3. Copy or download the Markdown, or make a share link.
  4. Open http://[ZIMAOS-IP-ADDRESS]:6132/help for the API, the MCP address and the Claude skill.

Before you install

PullMD has no sign-in to start with, so anyone on your network can use it. To keep it to yourself, set PULLMD_AUTH_MODE to single-admin and PULLMD_ADMIN_EMAIL to your own email. Leave PULLMD_ADMIN_PASSWORD blank to get a random one, shown in the app log as "admin password is". PUBLIC_URL can stay as it is: PullMD follows the address you open it at. Change localhost to your ZimaOS IP, like http://192.168.1.20:6132, to get the right address in the help page and the Claude skill. Every other setting is optional and listed in the Variables tab. To use one, add it in the app settings with your value; a setting you leave out keeps its default. The first start takes about two minutes.

The settings in the first table are already in the app settings. To use any other one, open the app settings, add the variable with your value, save, and the app restarts with it. A setting you leave out uses its default.

In the app settings

VariableDefault valueDescription
TZ$TZYour time zone. It is filled in from your ZimaOS time zone. Leave it unless you need another.
PUBLIC_URLhttp://localhost:6132The address you open PullMD at, like http://[ZIMAOS-IP-ADDRESS]:6132 (for example http://192.168.1.20:6132), or your own https:// domain. PullMD works from any address without it. It is used for the links on the help page, the Claude skill download and OAuth sign-in for claude.ai. Keep the port at 6132.
PULLMD_AUTH_MODEdisabledWho can use PullMD. disabled = anyone who can reach it, no sign-in. single-admin = only you, with the admin email and password. multi-user = people make their own accounts.
PULLMD_ADMIN_EMAIL—The email for the first admin account, used when sign-in is on. Put your own email here. Left blank, it is [email protected].
PULLMD_ADMIN_PASSWORD—The password for the first admin account, used when sign-in is on. Leave it blank and the app makes a random one on first start. It is shown in the app log and saved in the data folder. Or type your own (at least 8 characters).

Sign-up and accounts

VariableDefault valueDescription
PULLMD_ALLOW_SIGNUPtrueOnly for PULLMD_AUTH_MODE=multi-user. Set to false to close the sign-up page, so only accounts you make can sign in.
DISABLE_PUBLIC_HISTORYfalseSet to true to hide the list of recent conversions and the archive, so people can't see what others fetched. Share links keep working.
OAUTH_JWT_SECRET—Turns on OAuth sign-in for claude.ai and Claude Desktop. Type a random text of at least 32 characters. Needs sign-in on and PUBLIC_URL set to the address claude.ai reaches.
PULLMD_AUTH_TOKEN—Old (v1) bearer token for single-admin mode. Only for old setups; make an API key in PullMD's settings instead.

Server

VariableDefault valueDescription
PULLMD_CACHE_RETENTION_DAYS90How many days a conversion and its share link are kept without a new fetch. 0 keeps everything forever.
PULLMD_ALLOWED_HOSTS—PullMD won't fetch addresses on your own network. List the ones it may fetch, separated by commas, like 192.168.1.0/24,wiki.home.
PULLMD_TRUST_PROXYloopbackWhich proxies may pass on the visitor's address. The built-in gateway is covered. Behind your own reverse proxy, add its address, like loopback,172.17.0.0/16.
PULLMD_MAX_FETCH_BYTES52428800The biggest page or document PullMD will fetch, in bytes. The default is 50 MB.
PULLMD_SITE_RECIPES—Your own site recipes. Put the file in the app's data/pullmd folder and type /data/site-recipes.json here.

AI for images and audio

VariableDefault valueDescription
PULLMD_LLM_API_KEY—One API key for both image captions and audio transcripts. The settings below for one kind win over it.
PULLMD_LLM_BASE_URL—The OpenAI-compatible address for that key, like https://api.groq.com/openai/v1. Blank means OpenAI.
PULLMD_VISION_API_KEY—API key for describing images. Image captions are off until a key is set here or in PULLMD_LLM_API_KEY.
PULLMD_VISION_BASE_URL—The OpenAI-compatible address for image captions, like https://api.anthropic.com/v1 or http://192.168.1.20:11434/v1 for Ollama. Blank means OpenAI.
PULLMD_VISION_MODELgpt-4o-miniThe model for image captions.
PULLMD_STT_API_KEY—API key for audio transcripts. Transcripts are off until a key is set here or in PULLMD_LLM_API_KEY.
PULLMD_STT_BASE_URL—The OpenAI-compatible address for audio transcripts. It must offer /audio/transcriptions. Blank means OpenAI.
PULLMD_STT_MODELwhisper-1The model for audio transcripts.
PULLMD_PDF_OCR_API_KEY—API key for the paid PDF reader that keeps tables (Mistral OCR). Used when a request asks for ?pdf=ocr.
PULLMD_PDF_OCR_BASE_URL—The address of the PDF reader. Blank means Mistral.
PULLMD_PDF_OCR_MODELmistral-ocr-latestThe model for the PDF reader.
PULLMD_LLM_RATE_LIMIT30How many paid AI calls each person or address may make per hour. 0 means no limit.
PULLMD_EMBEDDED_IMAGESon-requestDescribe the images in Reddit posts: off, on-request (when asked) or always. Needs an image caption key.
PULLMD_EMBEDDED_IMAGES_MAX4The most images described per post, 1 to 20.

YouTube

VariableDefault valueDescription
MARKITDOWN_YOUTUBEtrueSet to false to stop turning YouTube links into transcripts.
MARKITDOWN_YT_TIMECODESlinksHow timecodes look in transcripts: links, plain or none.
MARKITDOWN_YT_CHUNK30Seconds of speech per transcript block. 0 keeps YouTube's own lines.
MARKITDOWN_YT_LANGS—The transcript languages you prefer, separated by commas, like en,de. Blank takes the first one there is.
MARKITDOWN_YT_PROXY—An http(s) proxy for YouTube, like http://user:pass@proxy:8080, if YouTube blocks your address.

Documents

VariableDefault valueDescription
MARKITDOWN_CONVERT_TIMEOUT60How many seconds one document may take to convert. 0 means no limit.
MARKITDOWN_MEM_LIMIT_MB0The most memory one document conversion may use, in MB. 0 means only the app's memory limit counts.

Reddit

VariableDefault valueDescription
REDDIT_CLIENT_ID—The id of a "script" app from reddit.com/prefs/apps. With it, Reddit allows more requests per minute.
REDDIT_CLIENT_SECRET—The secret of that Reddit app.
REDDIT_USER_AGENT—The name PullMD gives Reddit, like linux:pullmd:1.0 (by u/yourname).

Fetching

VariableDefault valueDescription
PULLMD_USER_AGENT—One browser name to send to every site, instead of changing it each time.
PULLMD_UA_FEED_URLhttps://raw.githubusercontent.com/WinFuture23/real-world-user-agents/refs/heads/main/user-agents.jsonWhere PullMD gets its list of current browser names. Point it at your own list in the same format.

Output

VariableDefault valueDescription
PULLMD_SOURCE_HEADERfalseSet to true to put the title, site and date at the top of the Markdown, like version 2 did.
PULLMD_FRONTMATTER_FIELDS—Which front matter fields to include, separated by commas, like title,url,source,fetched. Blank includes all.
PULLMD_COVERAGE_GUARDonSet to off to stop PullMD from retrying pages where it only caught a small part of the text.
PULLMD_FILENAME_DATE_PREFIX—Put the date in front of downloaded file names, like YYYY-MM-DD-.
docker-compose.yml
name: pullmd

services:
  pullmd:
    image: ghcr.io/hsinghhira/pullmd:latest
    container_name: pullmd
    restart: unless-stopped
    stop_grace_period: 40s
    privileged: true
    environment:
      - TZ=$TZ
      - PUBLIC_URL=http://localhost:6132
      - PULLMD_AUTH_MODE=disabled
      - PULLMD_ADMIN_EMAIL=
      - PULLMD_ADMIN_PASSWORD=
    ports:
      - target: 8080
        published: "6132"
        protocol: tcp
    volumes:
      - type: bind
        source: /DATA/AppData/$AppID/data
        target: /data
        bind:
          create_host_path: true
    healthcheck:
      test: ["CMD", "curl", "-fsS", "-o", "/dev/null", "http://127.0.0.1:8080/api/status"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 120s
    deploy:
      resources:
        limits:
          cpus: "2.00"
          memory: 2048M
        reservations:
          cpus: "0.00"
          memory: 256M
    labels:
      icon: https://cdn.jsdelivr.net/gh/HSinghHira/IceStore@main/Apps/pullmd/icon.png
    x-casaos:
      id: com.icestore.pullmd
      envs:
        - container: TZ
          description:
            en_US: >-
              Your time zone. It is filled in from your ZimaOS time zone.
              Leave it unless you need another.
        - container: PUBLIC_URL
          description:
            en_US: >-
              The address you open PullMD at, like
              http://[ZIMAOS-IP-ADDRESS]:6132 (for example
              http://192.168.1.20:6132), or your own https:// domain.
              PullMD works from any address without it. It is used for the
              links on the help page, the Claude skill download and OAuth
              sign-in for claude.ai. Keep the port at 6132.
        - container: PULLMD_AUTH_MODE
          description:
            en_US: >-
              Who can use PullMD. disabled = anyone who can reach it, no
              sign-in. single-admin = only you, with the admin email and
              password. multi-user = people make their own accounts.
        - container: PULLMD_ADMIN_EMAIL
          description:
            en_US: >-
              The email for the first admin account, used when sign-in is on.
              Put your own email here. Left blank, it is [email protected].
        - container: PULLMD_ADMIN_PASSWORD
          description:
            en_US: >-
              The password for the first admin account, used when sign-in is
              on. Leave it blank and the app makes a random one on first
              start. It is shown in the app log and saved in the data folder.
              Or type your own (at least 8 characters).
      ports:
        - container: "8080"
          description:
            en_US: PullMD web app, REST API and MCP server, published on host as 6132
      volumes:
        - container: /data
          description:
            en_US: >-
              The conversion cache and share links, accounts, API keys and
              the admin password.

x-casaos:
  id: com.icestore.pullmd
  architectures:
    - amd64
    - arm64
  main: pullmd
  author: HSinghHira
  category: AI
  developer: AeternaLabsHQ
  icon: https://cdn.jsdelivr.net/gh/HSinghHira/IceStore@main/Apps/pullmd/icon.png
  thumbnail: https://cdn.jsdelivr.net/gh/HSinghHira/IceStore@main/Apps/pullmd/screenshot-1.png
  screenshot_link:
    - https://cdn.jsdelivr.net/gh/HSinghHira/IceStore@main/Apps/pullmd/screenshot-1.png
    - https://cdn.jsdelivr.net/gh/HSinghHira/IceStore@main/Apps/pullmd/screenshot-2.png
  title:
    en_US: PullMD
  tagline:
    en_US: Anything in, clean Markdown out. Web pages, documents and YouTube to Markdown for people and AI agents.
  description:
    en_US: >-
      PullMD turns web pages, documents, images, audio and YouTube videos into
      clean Markdown. Use it from the web app, the REST API, the MCP server
      or a Claude skill. This is PullMD in a single container, with one port
      and one data folder. The Trafilatura and MarkItDown helpers are inside.
      The Playwright helper (headless Chromium, about 3.7 GB) is left out, so
      pages that need JavaScript fall back to plain extraction.


      **Features**


      - Web pages through Cloudflare Markdown, Readability and Trafilatura

      - Reddit threads and Hacker News discussions with their comments

      - PDF, Word, PowerPoint, Excel and EPUB, by link or upload

      - YouTube title, description and transcript with timecodes, no API key

      - Image captions and audio transcripts with any OpenAI-compatible model
        (optional)

      - Only the parts that match a question (?query=), to save tokens

      - Share links that keep the last good copy if the page goes away

      - MCP server for AI assistants, and optional sign-in with API keys


      **How to use?**


      1. Open PullMD from the Zima OS dashboard.


      2. Paste a link, or drop a file, and press Convert.


      3. Copy or download the Markdown, or make a share link.


      4. Open /help for the API, the MCP address and the Claude skill.
  tips:
    before_install:
      en_US: >-
        1. Open the app configuration before you install.


        2. PullMD has no sign-in to start with. Anyone on your network can use
           it.


        3. To keep it to yourself, set PULLMD_AUTH_MODE to single-admin. Set
           PULLMD_ADMIN_EMAIL to your own email.


        4. Leave PULLMD_ADMIN_PASSWORD blank to get a random one. Or type your
           own.


        5. PUBLIC_URL can stay as it is. Change localhost to your ZimaOS IP,
           like http://192.168.1.20:6132, to get the right address in the
           help page and the Claude skill.


        6. More settings, like AI keys for images and audio, YouTube options
           and Reddit keys, are listed on the PullMD page at
           https://icestore.hsinghhira.me/app/pullmd/ (Variables tab). You
           can add them now or later in the app settings.


        7. Start the installation.


        8. Wait about two minutes for all parts to start.


        9. If you turned sign-in on and left the password blank, open the app
           log. Find the line that says "admin password is".


        10. Open http://[ZIMAOS-IP-ADDRESS]:6132 in your browser.
  index: /
  port_map: "6132"
  scheme: http
  is_uncontrolled: false
  version: "3.16.0"
  update_at: "2026-10-28"
  release_notes:
    en_US: |-
      - Single-container PullMD with one port (6132) and one data folder
      - aeternalabshq/pullmd 3.16.0
      - aeternalabshq/pullmd-trafilatura 3.16.0
      - aeternalabshq/pullmd-markitdown 3.16.0
      - YouTube transcripts are on (upstream: off), since MarkItDown is inside
      - Left out: the Playwright helper (headless Chromium, about 3.7 GB)
  repo: "https://github.com/AeternaLabsHQ/pullmd"
  support: "https://github.com/AeternaLabsHQ/pullmd/issues"
  docs: "https://github.com/AeternaLabsHQ/pullmd#readme"

Install PullMD

Something not working with PullMD? Report a problem or request another app.

More AI apps

See all