Psych is Ruby's YAML engine, and it now has a second backend
A libyaml wrapper for Ruby
At a glance
- What is it?
- Psych is the YAML parser and emitter that ships with Ruby, wrapping libyaml by default and offering an experimental, opt-in backend built on libfyaml that follows YAML 1.2, fixes the Norway problem, and emits more slowly than the default.
- Who is it for?
- Psych suits any Ruby project that already loads YAML, where the only decision to make is whether to stay on the default libyaml backend or opt into libfyaml for YAML 1.2 semantics. It is the wrong choice if you need byte-identical output across backends, since the two emitters format differently, and the wrong choice if you are emit-bound rather than parse-bound, because the libfyaml emitter is markedly slower.
- 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 9 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 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
You already have it, and there is a gem too
Psych is a YAML parser and emitter. It uses libyaml for its YAML parsing and emitting capabilities, and in addition to wrapping libyaml, Psych also knows how to serialize and de-serialize most Ruby objects to and from the YAML format.
The second half of that sentence is the part that is specific to Ruby. Psych is more than a YAML document library, it is the object mapping layer on top of one, which is why it sits in the standard library and why it has a different character from a generic YAML binding.
The history explains the packaging. Psych has been included with MRI since 1.9.2, and it has been the default YAML parser since 1.9.3. So for most Ruby code, requiring psych loads the version that ships with the interpreter, and installing the gem is only interesting when you want a newer release than your Ruby carries.
The installation path is the standard one:
gem install psychAnd to make sure the gem is used rather than the stdlib copy, you declare it and require it:
gem 'psych'
require 'psych'or, with Bundler, you add it to your Gemfile:
gem 'psych'The JRuby situation is different, and the README states it plainly: JRuby ships with a pure Java implementation of Psych. That is a reasonable design for a JVM runtime, and it also means the libyaml and libfyaml backends discussed below are a CRuby story.
Safe loading and dumping, in two lines
The examples in the README are short enough to be the whole API in miniature:
# Safely load YAML in to a Ruby object
Psych.safe_load('--- foo') # => 'foo'
# Emit YAML from a Ruby object
Psych.dump("foo") # => "--- foo\n...\n"Two behaviours are visible in those lines. The first is that safe_load is the sensible entry point for untrusted input, because a plain YAML load can instantiate arbitrary Ruby objects, and the safe variant refuses to do that. The second is the shape of Psych's output, with the document start marker and the document end marker, which matters for anyone diffing generated YAML.
The libyaml dependency is the default, and the second dependency is optional: libfyaml, and only for the experimental backend. The README is also clear that Psych supported a static build against a specific version of libyaml sources, and shows how to build against a particular one:
gem install psych -- --with-libyaml-source-dir=/path/to/libyaml-0.2.5That option is what you reach for when a system libyaml is too old for what the build needs, or when you want the build to be reproducible against a known libyaml source tree rather than whatever the machine happens to have installed. It also means the compiled extension is not portable between machines with different libyaml versions, which is worth remembering when a build works locally and fails in a container.
The libfyaml backend is opt-in and experimental
Psych ships an experimental, opt-in backend built on libfyaml, a fully YAML 1.2 compliant parser and emitter. It is compiled only when you explicitly pass --enable-libfyaml at build time, and without the flag the default libyaml backend is used and nothing changes.
# libfyaml and pkg-config must be installed first, for example:
# apt-get install libfyaml-dev # Debian/Ubuntu
# brew install libfyaml # macOS
gem install psych -- --enable-libfyamlThe build dependency is real, since libfyaml and pkg-config have to be present before the extension compiles, and the backend is not supported on Windows.
The reason to want it is YAML 1.2 semantics. Because libfyaml follows YAML 1.2, the YAML 1.1 booleans yes, no, on and off load as plain strings instead of true and false, since only true and false are booleans. That resolves the so-called Norway problem, where the country code no was parsed as false:
Psych.load("country: no") # => {"country" => "no"}For a project that ships configuration files to users, that is not a curiosity. A YAML 1.1 parser turns a legitimate value into a boolean, and the fix usually ends up as a quoting workaround in the documentation rather than in the parser.
Which backend is active is inspectable at runtime:
Psych::BACKEND # => "libfyaml" (or "libyaml")
Psych.libfyaml_version # => "0.9.6"The version accessor is worth having for a different reason. If a deployment mixes a stdlib build and a gem build across machines, this is how you find out which one is actually running.
What changes when you switch backends
The README is honest that the backend is experimental, and it lists the differences that would otherwise show up as mysterious behaviour in production.
Output is formatted differently from libyaml in places, and a few emitter edge cases are not yet matched. The default libyaml backend remains the supported choice. Scalars emitted with the default ANY style may be quoted or laid out differently from libyaml, so byte-for-byte output is not guaranteed to match. That last point is the one that breaks test suites, because a test that asserts on a generated YAML file will fail on formatting alone even when the data is identical.
Error handling changes shape too. On a parse error, Psych::SyntaxError#problem carries libfyaml's full diagnostic message and Psych::SyntaxError#context is always nil, whereas libyaml splits the description across #problem and #context. Any code that reads both fields, or that treats a nil context as a bug, will behave differently under the new backend.
So the switch is not just a compile flag. It is a decision about output stability, error handling semantics and, as the next section shows, throughput.
Parsing at parity, emitting noticeably slower
The README puts a rough single-machine benchmark on the record, and the shape of the result is the useful part rather than the exact figures.
In that benchmark, which loads and dumps in-memory documents, parsing was roughly on par with libyaml, and sometimes faster on string-heavy input. Emitting was about 1.7 to 1.9 times slower. The README notes that your numbers will vary but that the shape holds, and it draws the conclusion directly: libfyaml is competitive at parsing and slower at emitting.
The guidance that follows is unambiguous about which to choose. Use the libfyaml backend when you need YAML 1.2 semantics, and keep using the default libyaml backend if throughput is your priority.
That maps onto two very different workloads. A service that reads a large number of configuration documents and writes almost nothing is a good candidate for the new backend, since parsing is the operation it performs and parsing is where libfyaml is at least as good. A pipeline that reads a small input and emits a large amount of YAML, a generator of manifests or of chart data, is the opposite case, and it is the one that will feel the slowdown.
The honest framing is that libfyaml was chosen for compliance, not for speed, and the README says as much: it targets YAML 1.2 compliance, not speed.
Releasing, licensing and what the repository holds
The release process is short, because the project uses the trusted publisher and the rubygems/release-gem workflow:
git tag vXXX && git push origin vXXXA tag is all it takes, and the trusted publisher means the gem is signed by the project's own credentials rather than by a personal API key. That is a supply-chain property worth having in a library that most Ruby applications load without thinking about it.
The licence is MIT, with the copyright line reading Copyright 2009 Aaron Patterson, et al., and the full permission text is in the README and in the LICENSE file at the root.
The repository layout reflects a project that is both a gem and a standard library component. There is a lib directory for the Ruby side, an ext directory for the C extension that binds libyaml, a test directory, a bin directory, a Rakefile for the tasks, a Gemfile, a psych.gemspec for packaging, and a Mavenfile, which is how the JRuby side is built with Maven. A .git-blame-ignore-revs file is present, which is the convention for large mechanical changes that should not obscure authorship in the history.
The release cadence is quick, with v5.5.0 on 2026-08-21, v5.4.0 on 2026-07-03 and v5.3.1 on 2025-12-17, and the last push to the repository was on 2026-10-14. The library is not archived, and the mainline continues to ship.
Editorial conclusion
Psych suits any Ruby project that already loads YAML, where the only decision to make is whether to stay on the default libyaml backend or opt into libfyaml for YAML 1.2 semantics. It is the wrong choice if you need byte-identical output across backends, since the two emitters format differently, and the wrong choice if you are emit-bound rather than parse-bound, because the libfyaml emitter is markedly slower. Before switching backends, check whether your documents contain yes, no, on or off values, because that is the behaviour change you are actually buying, and confirm that the parse error handling your code depends on still works, since Psych::SyntaxError#context is always nil under libfyaml.
Frequently asked questions
What is Psych in Ruby?
Psych is a YAML parser and emitter. It uses libyaml for parsing and emitting, and in addition to wrapping libyaml it knows how to serialize and de-serialize most Ruby objects to and from YAML. It has shipped with MRI since 1.9.2 and has been the default YAML parser since 1.9.3.
How do I install a newer version of Psych?
Run gem install psych. To use the gem rather than the version shipped with Ruby, add gem 'psych' before require 'psych', or add gem 'psych' to your Gemfile. You can also build against a specific libyaml source tree with gem install psych -- --with-libyaml-source-dir=/path/to/libyaml-0.2.5.
What does the libfyaml backend change in Psych?
It follows YAML 1.2, so the YAML 1.1 booleans yes, no, on and off load as plain strings, which resolves the Norway problem where the country code no was parsed as false. Output formatting differs from libyaml in places, a few emitter edge cases are not yet matched, and Psych::SyntaxError#context is always nil because the full diagnostic is carried by #problem.
Is the libfyaml backend faster than libyaml?
It depends on the operation. In a rough single-machine benchmark, parsing was roughly on par with libyaml and sometimes faster on string-heavy input, while emitting was about 1.7 to 1.9 times slower. The README recommends the libfyaml backend when YAML 1.2 semantics matter and the default libyaml backend when throughput matters.
How is a new Psych version released?
The project uses the trusted publisher and the rubygems/release-gem workflow, so a release is git tag vXXX && git push origin vXXX. The gem is then signed by the project's own publishing credentials.
Official sources
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.
[](https://hysenlabs.com/projects/ruby-psych)Was this useful?