murtaza-nasir/pdf3md: Self-Hosted PDF to Markdown↗ Smart Converter & Word Converter
Converting PDF documents into editable, structured formats remains a persistent pain point for developers, technical writers, and data pipeline engineers. Most existing tools either trap content in proprietary formats, require expensive API credits, or produce Markdown so broken it needs manual cleanup. The tweet framing is simple and direct: "Converts PDFs into structured Markdown and Word files"—but the underlying need is deeper. Teams want ownership of their document pipelines, clean output that preserves structure, and deployment flexibility without vendor lock-in.
murtaza-nasir/pdf3md addresses this directly. It is an open-source, self-hostable web application with a React↗ Bright Coding Blog frontend and Python↗ Bright Coding Blog Flask backend, designed specifically for converting PDF documents to clean Markdown and Microsoft Word (DOCX) formats. With 400 GitHub stars, 40 forks, and active maintenance as of June 2025, it represents a pragmatic choice for teams prioritizing control over convenience. This article examines what murtaza-nasir/pdf3md offers, how to deploy it, and where it fits in the document processing landscape.
What is murtaza-nasir/pdf3md?
murtaza-nasir/pdf3md is a modern web application built for efficient PDF conversion, maintained by Murtaza Nasir and released under the GNU Affero General Public License v3.0 (AGPLv3). The project takes a dual-licensing approach, offering both open-source and commercial licensing options—a structure increasingly common among tools that aim to balance community access with sustainable development.
The architecture follows a clean separation: a React/Vite frontend handles the user interface and real-time progress display, while a Python Flask backend manages the actual document processing. This stack choice reflects practical engineering rather than trend-chasing. React provides a responsive, component-driven UI; Flask offers a lightweight, well-understood Python server; and the combination deploys cleanly via Docker↗ Bright Coding Blog Compose.
The tool's relevance stems from its specific focus. Unlike general-purpose document converters that treat PDF as one format among many, murtaza-nasir/pdf3md concentrates on the PDF→Markdown→DOCX pipeline. This matters because Markdown serves as the lingua franca for modern documentation systems—static site generators, GitHub repositories, knowledge bases, and LLM context windows all consume it natively. The DOCX export, powered by Pandoc, extends utility to teams still working in Microsoft Office workflows.
The project shows healthy maintenance signals: last commit dated 2025-06-16, CI/CD pipelines for both frontend and backend Docker images, and structured documentation. At 400 stars, it sits in the "emerging but validated" tier—large enough to suggest real usage, small enough that contributors can still shape its direction.
Key Features
PDF to Markdown Conversion with Structure Preservation
The core capability uses PyMuPDF4LLM to extract content while preserving structural elements—headings, lists, tables, and basic formatting. This is critical because naive PDF text extraction often produces a flat stream of paragraphs, destroying the hierarchical organization that makes documents usable. PyMuPDF4LLM specifically targets LLM-friendly output, which correlates with clean, semantic Markdown.
Markdown to DOCX Conversion via Pandoc
A secondary mode converts user-provided Markdown to Microsoft Word format. This uses Pandoc, the battle-tested universal document converter, ensuring high-fidelity output that respects formatting instructions. The two-way capability—PDF in, Markdown intermediate, DOCX out—creates flexibility for mixed workflows.
Multi-File Batch Processing
The application supports simultaneous upload and processing of multiple PDF files. Each file receives independent progress tracking, with status updates displayed in real time. This addresses a genuine operational need: document pipelines rarely process single files in isolation.
Drag-and-Drop Interface with Progress Visibility
The UI emphasizes usability without sacrificing technical transparency. Users can drag files or use traditional selection, then monitor conversion status per file. Post-conversion, the interface displays original filename, file size, page count, and timestamp—metadata useful for audit trails and quality verification.
Responsive, Modern Interface
Built with React and Vite, the frontend adapts across device sizes. While document conversion is primarily a desktop task, responsive design ensures accessibility for tablet-based workflows or quick checks on mobile.
Docker-First Deployment
The project treats Docker as the primary distribution mechanism, with pre-built images published to Docker Hub and a convenience script (docker-start.sh) abstracting common operations. Health checks, restart policies, and network isolation are preconfigured.
Use Cases
Technical Documentation Migration
Engineering teams frequently inherit PDF documentation—API references, architecture decision records, runbooks—that needs migration into version-controlled Markdown. murtaza-nasir/pdf3md provides a self-hosted path for bulk conversion, with structural preservation reducing manual reformatting. The Markdown output feeds directly into [INTERNAL_LINK: static site generators] or GitHub wikis.
Content Pipeline Preprocessing
Organizations building RAG (Retrieval-Augmented Generation) systems or training data pipelines need clean, structured text from PDF sources. The PyMuPDF4LLM integration specifically targets LLM-ready output, making this tool a preprocessing stage for knowledge base construction—without sending documents to third-party APIs.
Regulatory and Compliance Workflows
Industries with document retention requirements often archive reports in PDF but need editable formats for active work. The DOCX export enables transition to Microsoft Office for redlining, while the Markdown archive preserves a future-proof, text-based original. Self-hosting satisfies data residency constraints that cloud services cannot.
Academic and Research Text Extraction
Researchers working with published papers in PDF format can extract structured Markdown for note-taking systems (Obsidian, Notion, Logseq) or citation management. The batch processing handles literature reviews efficiently, and the local deployment keeps sensitive pre-publication work off external servers.
Mixed-Format Publishing Pipelines
Publishing teams that produce both web (Markdown-based) and print (Word-based) outputs from the same source material can use the tool's bidirectional conversion. A PDF received from a contributor becomes Markdown for the web pipeline; editorial revisions in Markdown export to DOCX for print layout.
Installation & Setup
The recommended deployment path uses pre-built Docker images. Below are the exact commands from the project documentation.
Prerequisites
- Docker Engine
- Docker Compose (typically included with Docker Desktop)
Production Deployment (Recommended)
Create a dedicated directory and prepare the required files:
mkdir pdf3md-app && cd pdf3md-app
Create docker-compose.yml with the following content:
services:
backend:
image: docker.io/learnedmachine/pdf3md-backend:latest
container_name: pdf3md-backend
ports:
- "6201:6201"
environment:
- PYTHONUNBUFFERED=1
- FLASK_ENV=production
- TZ=America/Chicago
volumes:
- ./pdf3md/temp:/app/temp # Creates a local temp folder for backend processing if needed
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:6201/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
frontend:
image: docker.io/learnedmachine/pdf3md-frontend:latest
container_name: pdf3md-frontend
ports:
- "3000:3000"
depends_on:
- backend
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
default:
name: pdf3md-network
Download and prepare the startup script:
# Download docker-start.sh from the repository
curl -O https://raw.githubusercontent.com/murtaza-nasir/pdf3md/main/docker-start.sh
chmod +x ./docker-start.sh
Start the application:
./docker-start.sh start
Access points:
- Frontend:
http://localhost:3000 - Backend API:
http://localhost:6201
Development Mode
For local source development with hot-reloading:
./docker-start.sh dev
This requires docker-compose.dev.yml from the cloned repository. Development access:
- Frontend (Vite dev server):
http://localhost:5173 - Backend API:
http://localhost:6201
Direct Docker Compose (Alternative)
If preferring standard docker compose commands:
docker compose pull
docker compose up -d
Stop with:
docker compose down
Manual Setup (Without Docker)
For direct execution or development:
git clone https://github.com/murtaza-nasir/pdf3md.git
cd pdf3md
Backend:
cd pdf3md
pip install -r requirements.txt
python app.py # Serves on http://localhost:6201
Frontend (new terminal):
cd pdf3md
npm install
npm run dev # Serves on http://localhost:5173
Convenience scripts are also available:
chmod +x ./start_server.sh ./stop_server.sh
./start_server.sh # Starts both services
./stop_server.sh # Stops both services
Real Code Examples
The README contains configuration examples rather than extensive API code. Below are the documented configurations with explanations.
Docker Compose Production Configuration
services:
backend:
image: docker.io/learnedmachine/pdf3md-backend:latest
container_name: pdf3md-backend
ports:
- "6201:6201"
environment:
- PYTHONUNBUFFERED=1
- FLASK_ENV=production
- TZ=America/Chicago
volumes:
- ./pdf3md/temp:/app/temp
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:6201/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Explanation: This defines the backend service using the pre-built image. The PYTHONUNBUFFERED=1 environment variable ensures Python output appears immediately in logs, critical for debugging containerized applications. FLASK_ENV=production disables debug features. The healthcheck uses curl to verify the Flask server responds on its default port. The volume mount creates persistent local storage for temporary processing files.
Nginx Reverse Proxy Configuration
location /api/ {
proxy_pass http://<BACKEND_IP_OR_HOSTNAME>:6201/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
Explanation: This Nginx location block routes /api/ requests to the backend service. The trailing slash in proxy_pass strips the /api prefix before forwarding, matching the backend's route expectations. Header passthrough preserves client information for logging and security. Replace <BACKEND_IP_OR_HOSTNAME> with the actual backend address—localhost if co-located, or a Docker service name if using container networking.
docker-start.sh Command Reference
./docker-start.sh start # Production mode with pre-built images
./docker-start.sh dev # Development mode with hot-reloading
./docker-start.sh stop # Stop all services
./docker-start.sh status # Check running services
./docker-start.sh logs # View aggregated logs
./docker-start.sh rebuild dev example.com # Rebuild with custom domain
./docker-start.sh help # Display all commands
Explanation: The convenience script abstracts Docker Compose complexity. The rebuild variant with domain parameter supports deployments where the frontend must know its public hostname for API routing. This reflects the project's design assumption that frontend and backend share a base domain, with API requests resolved relatively.
Note: The current README documentation emphasizes configuration over application code examples. The above represents the complete set of documented code patterns.
Advanced Usage & Best Practices
Reverse Proxy Deployment
The frontend detects domain-based access and routes API calls to /api on the same host. Your reverse proxy must handle this path routing. For Nginx, serve the frontend at / and proxy /api/ to port 6201. The backend's permissive CORS configuration (Access-Control-Allow-Origin: *) simplifies same-domain deployments but should be reviewed if exposing directly to the internet.
Network Access Patterns
The default Docker setup assumes same-host or LAN access. The frontend connects to backend using the same hostname but port 6201. For LAN access, ensure host firewall rules permit port 6201 inbound. More complex topologies—separate subdomains, API gateways, or cloud load balancers—require frontend rebuild with custom API base URL configuration, which the current Docker images do not expose as runtime environment variables.
Timezone Configuration
The backend container defaults to America/Chicago. Modify the TZ environment variable in docker-compose.yml to match your locale (e.g., America/New_York, Europe/London, Asia/Tokyo). This affects conversion timestamps and log entries.
Port Conflict Resolution
If ports 3000, 5173, or 6201 are occupied, the README does not document override mechanisms. Standard Docker Compose practice applies: modify the host-side port mapping in docker-compose.yml (e.g., "8080:3000"), then update any hardcoded frontend API references accordingly. For development, also update vite.config.js proxy configuration.
Volume Persistence
The ./pdf3md/temp:/app/temp mount creates local storage for backend processing. In production, monitor this directory growth—temporary files from batch conversions may accumulate. Consider adding log rotation or scheduled cleanup if processing high volumes.
Comparison with Alternatives
| Tool | Approach | Key Difference | Best For |
|---|---|---|---|
| murtaza-nasir/pdf3md | Self-hosted web app, PyMuPDF4LLM + Pandoc | Full ownership, no API costs, dual export formats | Teams with data residency needs, batch workflows, mixed Markdown/Word pipelines |
| Marker (VikParuchuri) | Local CLI/Python library, deep learning models | Higher accuracy on complex layouts, single-user focused | Individual researchers, highest-fidelity extraction priority |
| Pandoc alone | Command-line universal converter | Mature, handles many formats, no PDF input natively | Users already comfortable with CLI, multi-format needs beyond PDF |
| Cloud APIs (AWS↗ Bright Coding Blog Textract, Azure Document Intelligence) | Managed service, per-page pricing | Zero infrastructure, advanced table/form extraction | Organizations with cloud commitment, variable volume, compliance with cloud provider |
Trade-off analysis: murtaza-nasir/pdf3md occupies a specific niche—self-hosted, web-UI-driven, with the PDF→Markdown→DOCX pipeline as a coherent workflow. Marker offers superior extraction quality for complex academic papers but lacks the integrated DOCX export and web interface. Pandoc requires external PDF-to-text preprocessing. Cloud APIs eliminate operational burden but introduce ongoing costs and data egress concerns. The AGPLv3 license is a strategic consideration: it ensures source availability for network-deployed instances but requires compliance planning for proprietary integration.
FAQ
What license governs murtaza-nasir/pdf3md?
Dual-licensed: GNU AGPLv3 for open-source use, or commercial license for proprietary integration. You must choose one.
Can I run this without Docker?
Yes. Clone the repository, install Python dependencies for the Flask backend, and Node.js dependencies for the React frontend. Convenience scripts (start_server.sh, stop_server.sh) are provided.
Does it require internet access?
No for core conversion. Docker images can run air-gapped. The PyMuPDF4LLM and Pandoc processing is entirely local.
What PDF layouts work best?
Text-heavy documents with clear structural hierarchy—headings, lists, paragraphs. Complex multi-column layouts or heavily formatted tables may require post-processing.
Is there an API, or only the web UI?
The backend exposes HTTP endpoints used by the frontend. Direct API usage is possible but not extensively documented; inspect app.py for endpoint definitions.
How do I update to new versions?
With Docker: docker compose pull then docker compose up -d. The docker-start.sh script may add convenience commands for this in future versions.
Can I contribute code?
Currently, external contributions require a future CLA process not yet formalized. Bug reports and feature suggestions via GitHub Issues are welcome now.
Conclusion
murtaza-nasir/pdf3md delivers a focused, self-hosted solution for PDF-to-Markdown and Markdown-to-Word conversion. Its architecture—React frontend, Flask backend, PyMuPDF4LLM extraction, Pandoc rendering—reflects pragmatic technology choices that prioritize deployability and transparency over novelty. For teams needing document processing without cloud dependency, data residency compliance, or recurring API costs, it offers a credible open-source path.
The 400-star, actively maintained project sits at a sweet spot: mature enough for production use, small enough to inspect and modify. The AGPLv3 license demands attention—ensure your use case aligns with its network-copyleft provisions, or contact the maintainers for commercial licensing.
Deploy in minutes via Docker, process batches with real-time visibility, and own your document pipeline end-to-end. Explore the repository, review the code, and determine if murtaza-nasir/pdf3md fits your stack at https://github.com/murtaza-nasir/pdf3md.