I like to use Testcontainers to provide databases for tests in my Ruby projects. It is fast, and the test data is ephemeral. Or at least, that is what I thought.

I recently noticed that when I run my tests, my project creates and deletes a container as expected, but it also creates a volume that is not cleaned up. I run my tests frequently, so I had dozens of volumes taking up gigabytes of storage. I decided to investigate.

I found a comment on an issue in the Testcontainers NodeJS library that told me that some of the libraries have a method .withRemoveVolumes that instructs the implementation to clean up anonymous volumes after removing the container. However, this option is not available in the Ruby library. Instead, I found that when you call remove on a container, you have the opportunity to pass options that will ultimately be passed to the Docker engine.

Setting the v option to true instructs the Docker engine to remove anonymous volumes associated with the container. This was exactly what I needed!

There is another option force which tells the Docker engine to stop the container if it is running before removing it. I have adopted it as well so I can stop and remove the container in one call to the Docker engine.

Following is an excerpt of code from my test_helper.rb file. It sets up the container, removes it and exits if initialization fails, and removes it after all tests have run.

begin
  pg = Testcontainers::PostgresContainer.new("postgres:18").start

  # other initialization...

rescue => e
  warn("failed to initialize container: #{e}")
  pg&.remove(force: true, v: true)
  exit(1)
end

Minitest.after_run do
  pg&.remove(force: true, v: true)
end