MD2CV: Markdown to ATS-Friendly Resume

Convert Your Markdown Resume to ATS-Friendly PDF and DOCX

Introduction

As a developer, I spend most of my time writing in Markdown. It is simple, portable, and version-controllable. So when it came time to update my resume, I asked myself: why not write it in Markdown too?

The problem is that recruiters and Applicant Tracking Systems (ATS) do not accept Markdown files. They need PDF or DOCX formats with clean, parseable layouts. After searching for existing solutions and finding them either too complex or lacking customization options, I decided to build my own tool.

The result is MD2CV – an open-source Markdown to resume converter that generates ATS-friendly PDF and DOCX files with customizable templates.

In this post, I will walk you through the features, installation process, and practical usage of MD2CV.

What is MD2CV?

MD2CV is a command-line tool that converts Markdown files into professional resume documents. It is designed with the following goals in mind:

  • Simplicity: Write your resume in plain Markdown with YAML frontmatter for metadata
  • ATS Compatibility: Generated documents use standard fonts and clean layouts that ATS systems can parse
  • Multiple Formats: Export to both PDF and DOCX from a single source file
  • Template System: Choose from built-in templates or create your own
  • Docker Support: No dependency headaches – just run with Docker

The project is open-source and available on GitHub: github.com/cagatayuresin/md2cv

Why Markdown for Resumes?

Before diving into the technical details, let me explain why Markdown is an excellent choice for writing resumes:

Version Control

Your resume is a living document that evolves throughout your career. With Markdown, you can track every change using Git. Want to see what your resume looked like two years ago? Just check out an older commit.

Single Source of Truth

Instead of maintaining separate files for different formats, you maintain one Markdown file. Need a PDF? Generate it. Need a DOCX for that recruiter who specifically asked for Word format? Generate that too. Both come from the same source.

Focus on Content

Markdown forces you to focus on what matters: the content. No fiddling with margins, font sizes, or spacing while writing. Those decisions are handled by templates.

Portability

A Markdown file is just plain text. It opens anywhere, on any device, with any editor. Twenty years from now, you will still be able to read it.


Key Features

YAML Frontmatter for Metadata

MD2CV uses YAML frontmatter to store your contact information and metadata separately from the content:

---
name: "John Doe"
title: "Senior Software Engineer"
email: "john.doe@example.com"
phone: "+1 555 123 4567"
location: "San Francisco, CA"
linkedin: "linkedin.com/in/johndoe"
github: "github.com/johndoe"
website: "johndoe.dev"
---
# Professional Summary
Your content starts here...
Code language: YAML (yaml)

This separation allows templates to render contact information consistently while you focus on writing the main content.

Multiple Template Options

MD2CV includes three built-in templates:

TemplateDescriptionBest For
ats_classicTraditional design with serif fontsConservative industries, large corporations
modernContemporary look with blue accentsTech companies, startups
minimalClean, simple layoutDesign-focused roles, creative industries

Each template is fully customizable. You can modify the existing ones or create entirely new templates.

Dual Format Export

Generate both PDF and DOCX from a single command. The PDF is rendered using WeasyPrint for high-quality output with full CSS support. The DOCX is generated using python-docx with proper styling for Microsoft Word compatibility.

Docker-First Approach

The tool is designed to run in Docker, eliminating the need to install system dependencies like Pango or Cairo on your local machine. This ensures consistent output across different operating systems.


Installation

Prerequisites

You need Docker installed on your system. If you do not have Docker yet:

  • macOS: Download from docker.com or install via Homebrew with brew install --cask docker
  • Windows: Download Docker Desktop from the official website
  • Linux: Follow the installation instructions for your distribution

Getting MD2CV

Clone the repository:

git clone https://github.com/cagatayuresin/md2cv.gitcd md2cvCode language: Bash (bash)

Build the Docker image:

docker build -t md2cv .Code language: Bash (bash)

That is it. You are ready to convert resumes.


Usage Guide

Basic Conversion

To convert a Markdown resume to both PDF and DOCX:

docker run --rm \  
-v "$(pwd)/input:/app/input:ro" \  
-v "$(pwd)/templates:/app/templates:ro" \  
-v "$(pwd)/output:/app/output" \  
md2cv /app/input/my_resume.md --format allCode language: Bash (bash)

Let me break down this command:

  • docker run --rm: Run a container and remove it when done
  • -v "$(pwd)/input:/app/input:ro": Mount your input directory as read-only
  • -v "$(pwd)/templates:/app/templates:ro": Mount templates as read-only
  • -v "$(pwd)/output:/app/output": Mount output directory for generated files
  • md2cv: The image name
  • /app/input/my_resume.md: Path to your Markdown file inside the container
  • --format all: Generate both PDF and DOCX

Choosing a Template

Use the --template flag to select a specific template:

docker run --rm \  
-v "$(pwd)/input:/app/input:ro" \  
-v "$(pwd)/templates:/app/templates:ro" \  
-v "$(pwd)/output:/app/output" \  
md2cv /app/input/my_resume.md --template modern --format pdfCode language: Bash (bash)

Listing Available Templates

To see all available templates:

docker run --rm \  
-v "$(pwd)/templates:/app/templates:ro" \  
md2cv --list-templatesCode language: Bash (bash)

Output:

Available templates:
----------------------------------------  
✓ ats_classic  
✓ modern  
✓ minimal

Format Options

FlagResult
--format pdfGenerate PDF only
--format docxGenerate DOCX only
--format allGenerate both formats

Writing Your Resume

Starting from the Template

The repository includes a sample template at examples/template_cv.md. Copy it to create your own:

cp examples/template_cv.md input/my_resume.mdCode language: Bash (bash)

Resume Structure

Here is the recommended structure:

---
name: "Your Name"
title: "Your Title"
email: "your.email@example.com"
phone: "+1 555 123 4567"
location: "City, Country"
linkedin: "linkedin.com/in/yourprofile"
github: "github.com/yourusername"
website: "yourwebsite.com"
---
# Professional Summary
A brief 2-3 sentence overview of your experience and expertise.
---
# Work Experience
## Job Title
**Company Name** | Location | Start Date - End Date
- Achievement using action verbs and metrics
- Another accomplishment with quantifiable results
- Third bullet point demonstrating impact
## Previous Job Title
**Previous Company** | Location | Dates
- Relevant achievements
- More accomplishments
---
# Education
## Degree Name
**University Name** | Graduation Year
- GPA: X.XX/4.00 (if notable)
- Relevant coursework or honors
---
# Skills
## Programming Languages
Python, JavaScript, Go, SQL
## Technologies
Docker, Kubernetes, PostgreSQL, Redis
## Languages
- English: Native
- Spanish: Intermediate
---
# Certifications
- **Certification Name** - Issuing Organization, Year
---
# Projects
## Project Name
Brief description of what you built and the technologies used.Code language: Markdown (markdown)

Formatting Tips

Use standard Markdown formatting:

  • # for main sections (Professional Summary, Work Experience, etc.)
  • ## for subsections (job titles, degrees)
  • **bold** for company names
  • - for bullet points
  • --- for horizontal rules between sections

The converter handles the translation to appropriate formatting in both PDF and DOCX output.


Creating Custom Templates

MD2CV uses Jinja2 for templating and CSS for styling. To create a custom template:

Step 1: Create a Template Directory

mkdir templates/my_custom_template

Step 2: Create template.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ meta.name }} - Resume</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <header>
            <h1>{{ meta.name }}</h1>
            <p class="title">{{ meta.title }}</p>
            <div class="contact">
                <span>{{ meta.email }}</span>
                <span>{{ meta.phone }}</span>
                <span>{{ meta.location }}</span>
            </div>
        </header>
        
        <main>
            {{ content | safe }}
        </main>
    </div>
</body>
</html>Code language: HTML, XML (xml)

Available template variables:

  • {{ meta.name }} – Full name
  • {{ meta.title }} – Job title
  • {{ meta.email }} – Email address
  • {{ meta.phone }} – Phone number
  • {{ meta.location }} – Location
  • {{ meta.linkedin }} – LinkedIn URL
  • {{ meta.github }} – GitHub URL
  • {{ meta.website }} – Personal website
  • {{ content | safe }} – Rendered Markdown content

Step 3: Create style.css

body {
    font-family: 'Helvetica', Arial, sans-serif;
    font-size: 11pt;
    line-height: 1.5;
    color: #333;
    max-width: 210mm;
    margin: 0 auto;
    padding: 20mm;
}
header {
    text-align: center;
    margin-bottom: 20px;
    border-bottom: 2px solid #333;
    padding-bottom: 15px;
}
h1 {
    font-size: 24pt;
    margin-bottom: 5px;
}
.title {
    font-size: 12pt;
    color: #666;
}
.contact span {
    margin: 0 10px;
}
main h1 {
    font-size: 14pt;
    text-transform: uppercase;
    border-bottom: 1px solid #ccc;
    margin-top: 20px;
}
main h2 {
    font-size: 12pt;
    margin-top: 15px;
}
ul {
    margin-left: 20px;
}
@media print {
    body {
        padding: 15mm;
    }
}Code language: CSS (css)

Step 4: Use Your Template

docker run --rm \  
-v "$(pwd)/input:/app/input:ro" \  
-v "$(pwd)/templates:/app/templates:ro" \  
-v "$(pwd)/output:/app/output" \  
md2cv /app/input/my_resume.md --template my_custom_templateCode language: Bash (bash)

Technical Implementation

For those interested in the technical details, here is how MD2CV works under the hood:

Architecture

The converter follows a straightforward pipeline:

  1. Parse: Read the Markdown file and extract YAML frontmatter
  2. Convert: Transform Markdown to HTML using the Python markdown library
  3. Render: Apply the selected Jinja2 template with metadata and content
  4. Export PDF: Use WeasyPrint to convert HTML/CSS to PDF
  5. Export DOCX: Parse Markdown directly and build a Word document using python-docx

Key Dependencies

LibraryPurpose
markdownParse Markdown to HTML
Jinja2Template rendering
WeasyPrintHTML/CSS to PDF conversion
python-docxDOCX generation
PyYAMLYAML frontmatter parsing

Why WeasyPrint?

WeasyPrint was chosen for PDF generation because it provides excellent CSS support, including:

  • Flexbox layouts
  • Web fonts
  • Print-specific media queries
  • Page break control

This allows templates to use modern CSS while producing high-quality PDF output.

Why Docker?

WeasyPrint requires system libraries (Pango, Cairo) that can be difficult to install on some systems. Docker encapsulates these dependencies, ensuring the tool works identically regardless of the host operating system.


ATS Compatibility Tips

When writing your resume, keep these tips in mind for maximum ATS compatibility:

Do

  • Use standard section headings (Work Experience, Education, Skills)
  • Stick to common fonts (Arial, Georgia, Times New Roman)
  • Use bullet points for achievements
  • Include keywords from job descriptions
  • Keep formatting simple and consistent

Avoid

  • Tables for layout (ATS systems struggle to parse them)
  • Headers and footers with important information
  • Graphics or images
  • Unusual fonts or icons
  • Multiple columns

The built-in templates are designed with these guidelines in mind, especially ats_classic which prioritizes parseability over visual flourish.


Comparison with Other Tools

FeatureMD2CVLaTeX ResumeOnline Builders
Plain text sourceYesYesNo
Version control friendlyYesYesNo
No account requiredYesYesNo
DOCX exportYesLimitedVaries
Custom templatesYesYesLimited
Learning curveLowHighLow
Runs offlineYesYesNo

Roadmap

Planned features for future releases:

  • Additional built-in templates
  • Cover letter support
  • Multiple language support in templates
  • Web interface for non-technical users
  • Template marketplace

Conclusion

MD2CV bridges the gap between the developer-friendly Markdown format and the recruiter-expected PDF and DOCX formats. It maintains the benefits of plain text source files while producing professional, ATS-compatible output.

If you are a developer who prefers writing in Markdown and wants full control over your resume format, give MD2CV a try. The project is open-source and contributions are welcome.

Repositorygithub.com/cagatayuresin/md2cv

License: MIT