moov-io/ach: reading, writing and validating NACHA ACH files in Go
ACH implements a reader, writer, and validator for Automated Clearing House (ACH) files. The HTTP server is available in a Docker image and the Go package is available.
At a glance
- What is it?
- The ach library implements a reader, writer and validator for Automated Clearing House files following the Nacha standards, the primary method of electronic money movement in the United States. It ships as a Go package, an HTTP server in a scratch-based Docker image, a command line tool and an in-browser parser, and the repository is organised one file per ACH record type, which tells you a lot about the shape of the format.
- Who is it for?
- Adopt moov-io/ach if you are building software that has to read, write or validate NACHA ACH files, because it supports all Standard Entry Class codes, ships as a Go package, an HTTP server, a command line tool and a browser parser, and is a mature 1.x project with several releases a month.
- Can I use it commercially?
- Yes. Apache-2.0 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 1 day ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the library is, and what ACH actually is
Moov's stated mission is to give developers an easy way to create and integrate bank processing into their own software products, and the open source projects under it are each focused on solving a single responsibility in financial services and designed around performance, scalability and ease of use.
This one is the Automated Clearing House. ACH implements a reader, writer and validator for ACH files following the Nacha standards, and the README states plainly that ACH is the primary method of electronic money movement throughout the United States. The HTTP server is available in a Docker image and the Go package github.com/moov-io/ach is available.
That last framing is the point of the library. An ACH file is a fixed-width plain text format, and a file is a sequence of records: a file header, batches of entries, and batch control records, ending with a file control record. Because the format is fixed-width and the Nacha rules are strict, reading and writing one correctly is a job with a lot of small rules in it, and that is exactly the kind of job a library is for.
The project status note says that Moov ACH is actively used in multiple production environments, and that the project supports generating and parsing all Standard Entry Class codes and validating files according to the Nacha standards. It also asks for issues on bugs, or for feedback on layers above ACH that simplify tasks or perform business operations.
The scope of the library is worth being careful about. This is a file format library. It reads, writes and validates ACH files. It does not move money, and it does not talk to banks. If you want that, the README points to moov-io/achgateway, an event driven ACH engine for uploading and downloading files and operations which the team runs in production, and to an article on when and when not to use the library.
The primary language is Go, the licence is Apache-2.0, the default branch is master, and the homepage is moov-io.github.io/ach. The recent releases are v1.64.0 from 2026-10-07, v1.63.7 from 2026-09-24 and v1.63.6 from 2026-09-21, and the last push was on 2026-10-20 with the project not archived.
How the repository is organised, and why that matters
The top-level listing is unusual and extremely informative. The root of the repository is a long list of Go files named after ACH record types: addenda02.go, addenda05.go, addenda10.go through addenda18.go, addenda98.go, addenda99.go, advBatchControl.go, advEntryDetail.go, advFileControl.go, ascii.go, batch.go, batchACK.go, batchADV.go, batchARC.go, and a long tail of the same pattern.
The naming pattern encodes the format itself. A prefix of adv means an Automated Entry Date, addenda02 is the second addenda record type, batchACK and batchADV are the acknowledgement and advance record types, and the numeric suffixes are the Nacha addenda record type codes. So the repository layout is a direct transcription of the Nacha specification into Go file names.
There is a subtlety in two of those files that shows how carefully the naming was chosen. addenda98_refused.go and addenda99_contested_return.go, addenda99_dishonored_return.go and addenda99_refused.go each carry their own file, and there is a separate addenda98_refused_test.go and addenda99_test.go. In Nacha, addenda 98 and 99 are return records, and a return carries a reason code that determines whether the return is contested or dishonoured. Splitting them across files rather than branching inside one is what keeps the parser readable.
The naming is camelCase for the record type files and snake_case for the addenda files, which is an inconsistency in the naming convention but a legible one, since the snake_case files are the record types with Nacha numeric codes.
The directories are the supporting parts: addenda/ for tests and fixtures, cmd/ for the binaries, and a long set of example files under examples/ that are named by transaction type, such as example_ackRead_test.go, example_advWrite_test.go, example_atxRead_test.go, example_cieWrite_credit_test.go, example_ctxWrite_debit_test.go and example_customReturnCode_test.go. Those examples are the documentation in executable form, and the read and write variants for each transaction type are exactly what you want to look at when you need to know how a particular SEC code is expressed.
The scale is conveyed by the sheer number of files. This is not a small utility; it is a full implementation of the format, and the file-per-record-type layout is what keeps that from becoming unreadable.
Running the HTTP server from Docker
The Docker route is the easiest way to see the library working, and it needs no configuration at all.
We publish a public Docker image moov/ach from Docker Hub, and the README states that no configuration is required to serve on port 8080 with metrics at port 9090 in Prometheus format. The pull and start commands are:
docker pull moov/ach:latest
docker run -p 8080:8080 -p 9090:9090 moov/ach:latestThe second port is the one to notice. Serving metrics in Prometheus format by default means the container is immediately scrapable, which is what makes it usable in an existing monitoring stack rather than something you have to instrument yourself.
The API is a small set of endpoints over files held in memory. Listing the files stored in memory:
curl localhost:8080/fileswhich returns an empty list on a fresh start. Creating a file on the HTTP server:
curl -X POST --data-binary "@./test/testdata/ppd-debit.ach" http://localhost:8080/files/createThe response gives you back a unique file id, and you then read the parsed file back in JSON form:
curl http://localhost:8080/files/<YOUR-UNIQUE-FILE-ID>The JSON contains the file header with fields like immediateDestination and immediateOrigin, and from there the batches, entries and addenda records.
A compose file is also provided, and it is worth a look because it shows the intended port mapping:
version: '3'
services:
ach:
image: moov/ach:latest
ports:
- "8080:8080"
- "8090:8090"The second port here is 8090 rather than 9090, which is a small discrepancy against the prose that says metrics are served on 9090. Whether that is a typo or an intentional override in the compose file is not explained, and it is the kind of thing to check before you rely on a metrics scrape path.
The Dockerfile is where the project's approach to images becomes clear. The build stage is golang:1.27-alpine with git and make added, the source is copied in, make build produces the binary, and the build then switches to a non-root user. The runtime stage is FROM scratch, so there is no operating system in the image at all, only the CA certificates copied over, the server binary, and the passwd entry for the moov user.
A scratch image is the right choice for a server that only needs to make outbound HTTPS connections to reach a bank or a payment partner, and it is a much smaller attack surface than a full Alpine base. The trade-off is that there is no shell in the container, so debugging it means rebuilding with a shell attached.
The Go library, the CLI and the browser parser
The Usage section describes the project as implementing an HTTP server and Go library for creating and modifying ACH files, and lists client libraries for both Go and Node and JavaScript, plus an extensive list of examples of the reader and writer applied to various ACH transaction types.
The Go package is the core, and it is the thing to reach for when you are embedding ACH into an application. The go.mod pins the module at github.com/moov-io/ach with Go 1.26.0 and a toolchain of go1.27.2, and the dependency list is short and tells you what the library leans on.
There is aws-lambda-go at 1.55.1, which is how the Lambda deployment works. There is moov-io/base, the shared base library, along with moov-io/iso3166 and moov-io/iso4217, the country and currency code lists, which an ACH file needs for address and currency fields. Prometheus client_golang is there for the metrics, gorilla/mux for routing, and go-kit for the logging and instrumentation layer.
Two dependencies deserve a note. go-randomdata is a test helper for generating plausible data, and treemap/v2 from igrmk is a terminal tree renderer, which is a strong hint about the command line tool: it prints a tree of the file structure rather than a flat dump.
The command line tool is the third interface, and the README's Google Cloud Run section shows it being used, starting the ACH server inside a cloned repository with:
go run ./cmd/serverrThe other interface is the in-browser parser, which is listed in the table of contents. That is a browser-side ACH file reader, which is unusual for a format that carries routing numbers and account numbers, and it is the sort of feature that needs a careful look before you assume it is appropriate for your data.
So there are four ways in: a Go package for embedding, an HTTP server for services, a command line tool for scripts and local work, and a browser parser. Choosing between them is mostly a question of where the file data is allowed to go, which is a question about your own compliance requirements as much as about the library.
Deploying to Google Cloud Run, and the trust prompt
The Google Cloud Run section is unusually detailed, and the detail is about a failure mode rather than the happy path.
The flow is: from the Google Cloud dashboard create a new project and call it moov-ach-demo, then click the button to deploy the project to Google Cloud. The deploy button points at the master revision of the repository.
Then there is the note about an error about the image being marked as Do Not Trust, and a details block that reproduces it. The error text is the important part:
$ cloudshell_open --repo_url "https://github.com/moov-io/ach" --page "shell" --git_branch "master"
Error: You launched this custom Cloud Shell image as "Do not trust".
In this mode, your credentials are not available and this experience
cannot deploy to Cloud Run. Start over and "Trust" the image.
Error: aborting due to untrusted cloud shell environmentThe cause is that some security settings on the account or cloud shell are locked down, and in this mode the credentials are not available and the experience cannot deploy to Cloud Run. The fix is to trust the image: in the top-right, restart the image as Trusted, and then click to Return to default.
The README is explicit that to run ACH you need to trust the image. That is a meaningful instruction, because a Do Not Trust Cloud Shell image cannot reach your Google Cloud credentials, and the reason the deployment fails is that the environment is locked down for a reason.
After trusting, the documented path is to clone the project with cloudshell_open, then start the server inside the cloned repository with the go run command, and then connect to the web preview at an URL of the form https://YOUR-ACH-APP-URL.a.run.app:8080/files. The README asks you to pick option 3 to clone the project, which implies the Cloud Shell menu offers more than one path.
The Cloud Run route is a one-button demo rather than a production deployment guide. For anything real, the Docker image and the compose file are the more useful starting points, and the container runs as a non-root user in a scratch image, which is a reasonable posture for a service that handles payment files.
How to evaluate it, and the questions to ask first
There are four things to establish, and the first is about scope.
This is a file format library, not a payment system. It reads, writes and validates ACH files; it does not move money. If you need to originate files and have them submitted to your ODFI, the README points to achgateway, an event driven engine the team runs in production, and to an article on when to use the library instead. Choosing the library for a job that needs the gateway is a common mistake, and it is easy to make because the library's own name does not say what it does not do.
Second, which transaction types you need. The library supports generating and parsing all Standard Entry Class codes, which is the broadest possible answer, and the examples directory shows the read and write forms for many of them. The point to check is the return and addenda handling, since that is where Nacha gets detailed, and the presence of separate files for contested, dishonoured and refused returns suggests it is handled rather than glossed over.
Third, the security posture of the data. ACH files contain routing numbers, account numbers and names, so the question of where the file is parsed matters. The Go package keeps everything in your process, the HTTP server can hold files in memory as the endpoints show, the command line tool keeps it local, and the in-browser parser sends the file to a browser. Those are very different exposures and the choice should be made deliberately.
Fourth, how the service will run. The Docker image is scratch-based and runs as a non-root user, metrics are exposed in Prometheus format on a second port, and there is a compose file. If you are deploying this in Kubernetes or anywhere with an existing scrape configuration, that is a short path.
On the maintenance side the picture is good. Three releases in a month, a push from two days ago, a Go Report Card badge, a build badge, and an AGENTS.md at the root indicating the project is worked on with tooling that includes automated agents. The version number being in the 1.6 range rather than 0.x means the API is considered stable, which for a library other people's money data passes through is the property you want.
The one thing to watch is the discrepancy between the port in the prose and the port in the compose file, and more generally the fact that the README's example responses are truncated with an ellipsis. Test the endpoints you intend to use rather than trusting the shape of the sample output.
Editorial conclusion
Adopt moov-io/ach if you are building software that has to read, write or validate NACHA ACH files, because it supports all Standard Entry Class codes, ships as a Go package, an HTTP server, a command line tool and a browser parser, and is a mature 1.x project with several releases a month. Do not adopt it as a payment system, because it moves no money and the project points to achgateway for that, and choose the interface by where the file data is allowed to go, since an in-browser parser and an in-process library have very different exposures for data containing account and routing numbers. Before you commit, test the endpoints you plan to use rather than trusting the truncated sample responses in the README, check the metrics port against the compose file because the prose and the compose mapping differ, and if you deploy it, rely on the scratch-based image running as a non-root user with Prometheus metrics already exposed.
Frequently asked questions
What is moov-io/ach?
It is a Go library and HTTP server that implements a reader, writer and validator for Automated Clearing House files following the Nacha standards, the primary method of electronic money movement in the United States. The Go package is github.com/moov-io/ach, and the HTTP server ships as a Docker image, an Apache-2.0 licensed project from Moov.
Does the ach library move money?
No. It reads, writes and validates ACH files, and it is described as a file format library. For an event driven engine for uploading and downloading files and running operations, the project points to moov-io/achgateway, which the team runs in production.
How do I run the ACH HTTP server?
Pull moov/ach:latest from Docker Hub and run it with ports 8080 and 9090 mapped. No configuration is required, and metrics are served on port 9090 in Prometheus format. You can then list files with curl localhost:8080/files, create one by posting the file bytes to /files/create, and read the parsed file back in JSON from /files/<YOUR-UNIQUE-FILE-ID>.
Which ACH transaction types are supported?
The project states that it supports generating and parsing all Standard Entry Class codes and validating files according to the Nacha standards. The examples directory shows read and write forms for many transaction types, including acknowledgements, advances, returns, and credit and debit variants of the CCD, CTX, CIE, ATX, BOC, ARC and DNE types.
How is the ach Docker image built?
The Dockerfile builds in a golang:1.27-alpine stage with git and make, runs make build, and switches to a non-root moov user. The runtime stage is FROM scratch, copying only the CA certificates, the server binary and the passwd entry, and the entrypoint is /bin/server with ports 8080 and 9090 exposed.
What are the ways to use the ach project?
There are four: as an HTTP server, as a Go module, as a command line tool, and as an in-browser ACH file parser. There are also client libraries for Go and for Node and JavaScript, and the project publishes OpenAPI SDKs.
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/moov-io-ach)Was this useful?