CLI tool
benbalter/word-to-markdown avatar
benbalter/word-to-markdown

word-to-markdown shells out to LibreOffice and is superseded

Ruby gem & CLI to convert Word (.docx) documents to Markdown. Superseded by word-to-markdown-js at word2md.com

1,549 stars155 forksRubyMIT

At a glance

What is it?
This Ruby gem converts .docx to Markdown by handing the file to LibreOffice and parsing the HTML it produces, which is why it needs soffice installed and why it rejects anything that is not a Word document. The README now opens by pointing you at word-to-markdown-js instead, so the interesting question is when the Ruby one is still the right choice.
Who is it for?
Adopt this gem only inside an existing Ruby codebase that already has the gem installed, since the README itself recommends word-to-markdown-js for new projects and points to word2md.com. Do not build a new service on it if you can avoid depending on a LibreOffice process per conversion.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 19 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The mechanism is a subprocess you have to install

The gem does not parse the OOXML package itself. It calls LibreOffice, asks it to convert the document to HTML, and then works on the result, which the API example shows when `file.document.tree` returns a Nokogiri Document.

That single architectural choice explains most of the behaviour you will meet.

First, LibreOffice is a hard requirement. The README says you need to install LibreOffice, and later states the tool requires `soffice`, the command line interface, on Linux, Mac and Windows, with a pointer to the LibreOffice installation documentation. If you do not have it, nothing works.

Second, output fidelity is bounded by LibreOffice's HTML, not by anything the gem does. The supported list is short and concrete: paragraphs, numbered lists, unnumbered lists, nested lists, italic, bold, explicit headings chosen as Heading 1 or Heading 2 in Word, implicit headings detected from a larger font size relative to body text, images, tables and hyperlinks. Anything Word can express that is not on that list is out of scope, and the implicit heading rule in particular is a heuristic rather than a guarantee.

Third, conversion is a process launch, so it is slow and so it can hang. That is what the timeout setting exists for.

Install and convert from the command line

With LibreOffice present, installation is a single gem command:

bash
gem install word-to-markdown

The executable is `w2m`, and it writes Markdown to standard output:

bash
w2m path/to/document.docx

From Ruby, the entry point is a class constructor, and `to_s` gives you the Markdown:

ruby
file = WordToMarkdown.new("/path/to/document.docx")
file.to_s
=> "# Test\n\n This is a test"

That stdout behaviour makes the tool pipeline-friendly. Redirect it, chain it, or capture it in a Rack action without any extra plumbing.

For local development the repository ships a Compose setup, and the Dockerfile is where the LibreOffice dependency becomes visible in full. It builds on `ruby:4.0`, installs `libreoffice-writer`, and calls `soffice --version` during the build so a broken LibreOffice install fails at image build time rather than at the first conversion:

bash
docker compose build
docker compose run --rm app bundle exec w2m test/fixtures/em.docx

The Compose file mounts the repository into the container, so edits on the host are live in the container.

Two errors you will meet, and why they exist

The README documents both failure modes precisely, which makes them easy to handle.

`WordToMarkdown::Document::UnsupportedFormatError` is raised when the input is not a Word document. The detail that matters is how detection works: the gem identifies a Word file by its contents, not by its extension. So an HTML, ODT or RTF file, even though LibreOffice would happily open it, raises this error. Conversely, a `.docx` that is not really a Word document also fails. This is a deliberate narrowing of the input surface rather than a limitation, and it matters if you are building an upload endpoint.

`WordToMarkdown::TimeoutError` fires when LibreOffice takes longer than 60 seconds. The default is a hard ceiling, and LibreOffice is killed when it trips. It is configurable in two ways: setting `WordToMarkdown.timeout = 120` in code, or the `WORD_TO_MARKDOWN_TIMEOUT` environment variable, which is the one to reach for in a container.

Both are ordinary exceptions, so in a service you rescue them and return something sensible rather than letting a hung LibreOffice process hold a request open.

Untrusted documents can make requests on your behalf

This is the part of the README that deserves to be quoted rather than paraphrased. Word documents can link to remote resources, such as images, and LibreOffice fetches them while converting. If you convert documents from untrusted sources, for example files uploaded to a web service, the recommendation is to run the conversion without network access, in a container or sandbox with no outbound network, so a document cannot reach internal services or other hosts on your behalf.

That is a server-side request forgery through a document, and it is not hypothetical. The input vector is a file upload, the mechanism is a fetcher inside LibreOffice, and the target is your internal network. Running the converter in the same container as your application, with the same network reachability, is the mistake.

The Docker path in this repository offers a convenient local setup, but deployments handling untrusted documents still need to disable outbound network access.

The successor is already the recommendation

The README opens with an important callout telling readers to check out word-to-markdown-js, described as the newer, better successor, a modern actively maintained rewrite, and recommended for new projects, with this Ruby gem remaining available for existing users. The repository description says the same thing more plainly: superseded.

The hosted service at word2md.com runs on word-to-markdown-js, and the previous Ruby-based word-to-markdown-server is archived. So the direction is unambiguous. This gem is in maintenance, not in development, and choosing it is choosing the status quo.

The case for keeping it is narrow and legitimate. A Ruby application already depends on the gem, the conversion runs inside a pipeline you control, and replacing it means touching working code for no functional gain. The case for the JavaScript version is a maintained path forward, while the hosted converter offers a service you can call.

On maintenance: the last push was on 2026-10-04, and releases came as v1.2.0 and then v1.2.1 on 2026-09-30, a patch after a minor on the same day, with v1.1.9 the previous one from 2025-01-08. The repository carries `renovate.json` for dependency updates, `.rubocop.yml` for Ruby style, `.hadolint.yml` for the Dockerfile, and a `CLAUDE.md`. The licence is MIT, in LICENSE.md.

A note on that version pattern: v1.2.0 and v1.2.1 landing together suggests recent work went into release mechanics rather than features, which is what you would expect from a project whose own README tells new users to go elsewhere.

Editorial conclusion

Adopt this gem only inside an existing Ruby codebase that already has the gem installed, since the README itself recommends word-to-markdown-js for new projects and points to word2md.com. Do not build a new service on it if you can avoid depending on a LibreOffice process per conversion. Verify first that soffice is on PATH in your runtime environment, that 60 seconds is enough for your documents or that WORD_TO_MARKDOWN_TIMEOUT covers them, and that conversions of user-supplied files run without outbound network access.

Frequently asked questions

How do I install word-to-markdown?

Install LibreOffice first, since the gem requires the `soffice` command line interface, then run `gem install word-to-markdown`. The CLI is then available as `w2m path/to/document.docx` and writes Markdown to stdout.

What does word-to-markdown do with non-Word files?

It raises WordToMarkdown::Document::UnsupportedFormatError. The gem identifies Word documents by their contents rather than by file extension, so HTML, ODT and RTF inputs fail even though LibreOffice could open them.

How do I change the LibreOffice conversion timeout?

LibreOffice is killed after 60 seconds by default, raising WordToMarkdown::TimeoutError. Set `WordToMarkdown.timeout = 120` in code, or use the WORD_TO_MARKDOWN_TIMEOUT environment variable, which is the practical option in a container.

Is it safe to convert Word documents uploaded by users?

Not without a network boundary. Word documents can link to remote resources such as images, and LibreOffice fetches them during conversion, so the README recommends running untrusted conversions in a container or sandbox with no outbound network.

Should I use word-to-markdown or word-to-markdown-js?

The README of this repository recommends word-to-markdown-js for new projects and describes it as the modern actively maintained rewrite, while this Ruby gem remains available for existing users. The hosted converter at word2md.com also runs the JavaScript version.

Official sources

  1. benbalter/word-to-markdown on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/benbalter-word-to-markdown.svg)](https://hysenlabs.com/projects/benbalter-word-to-markdown)

Was this useful?