Deploying Rails app with Kamal - why is my image so big?

janmpeterka

Jan Peterka

Posted on March 14, 2024

Deploying Rails app with Kamal - why is my image so big?

I'm currently in process of rewriting app from Python/Flask to Ruby/Rails, and one of changes is in deployment process.

I decided to use containers and Kamal, as it's upcoming default for Rails.
Mind you, I have no prior experience with neither Docker nor deployment tools like Capistrano.

There are many things that I got stuck on (and I plan to write about them maybe later), but today I was trying to solve one problem - images Kamal generated were about 3GB.
Whoa, that's a lot! It takes a long time to build, to upload to registry, to download to server. It's huge waste of resources!

I had to dig into it, and here's what I learned:

To find out how big my images really are (and compare before/after some changes), there's docker images. It lists all my images and their sizes.

But whats inside?
I found out about this handy command to show me what's contained inside the image:
docker history registry.example/example-all:latest
which shows me something like this:

IMAGE          CREATED              CREATED BY                                      SIZE      COMMENT
211b6b39d381   About a minute ago   CMD ["./bin/rails" "server"]                    0B        buildkit.dockerfile.v0
<missing>      About a minute ago   EXPOSE map[3000/tcp:{}]                         0B        buildkit.dockerfile.v0
<missing>      About a minute ago   ENTRYPOINT ["/rails/bin/docker-entrypoint"]     0B        buildkit.dockerfile.v0
<missing>      About a minute ago   USER rails:rails                                0B        buildkit.dockerfile.v0
<missing>      About a minute ago   RUN /bin/sh -c useradd rails --create-home -…   74.9MB    buildkit.dockerfile.v0
<missing>      About a minute ago   COPY /rails /rails # buildkit                   2.7GB     buildkit.dockerfile.v0
<missing>      17 minutes ago       COPY /usr/local/bundle /usr/local/bundle # b…   148MB     buildkit.dockerfile.v0
<missing>      18 minutes ago       RUN /bin/sh -c apt-get update -qq &&     apt…   148MB     buildkit.dockerfile.v0
<missing>      18 minutes ago       ENV RAILS_ENV=production BUNDLE_DEPLOYMENT=1…   0B        buildkit.dockerfile.v0
<missing>      19 minutes ago       WORKDIR /rails                                  0B        buildkit.dockerfile.v0
<missing>      8 weeks ago          CMD ["irb"]                                     0B        buildkit.dockerfile.v0
<missing>      8 weeks ago          RUN /bin/sh -c mkdir -p "$GEM_HOME" && chmod…   0B        buildkit.dockerfile.v0
<missing>      8 weeks ago          ENV PATH=/usr/local/bundle/bin:/usr/local/sb…   0B        buildkit.dockerfile.v0
<missing>      8 weeks ago          ENV BUNDLE_SILENCE_ROOT_WARNING=1 BUNDLE_APP…   0B        buildkit.dockerfile.v0
<missing>      8 weeks ago          ENV GEM_HOME=/usr/local/bundle                  0B        buildkit.dockerfile.v0
<missing>      8 weeks ago          RUN /bin/sh -c set -eux;   savedAptMark="$(a…   58.1MB    buildkit.dockerfile.v0
<missing>      8 weeks ago          ENV RUBY_DOWNLOAD_SHA256=cfb231954b8c241043a…   0B        buildkit.dockerfile.v0
<missing>      8 weeks ago          ENV RUBY_DOWNLOAD_URL=https://cache.ruby-lan…   0B        buildkit.dockerfile.v0
<missing>      8 weeks ago          ENV RUBY_VERSION=3.2.3                          0B        buildkit.dockerfile.v0
<missing>      8 weeks ago          ENV LANG=C.UTF-8                                0B        buildkit.dockerfile.v0
<missing>      8 weeks ago          RUN /bin/sh -c set -eux;  mkdir -p /usr/loca…   45B       buildkit.dockerfile.v0
<missing>      8 weeks ago          RUN /bin/sh -c set -eux;  apt-get update;  a…   46.5MB    buildkit.dockerfile.v0
<missing>      8 weeks ago          /bin/sh -c #(nop)  CMD ["bash"]                 0B        
<missing>      8 weeks ago          /bin/sh -c #(nop) ADD file:b86ae1c7ca3586d8f…   74.8MB    
Enter fullscreen mode Exit fullscreen mode

Here I saw that my problem lies in COPY rails/ rails/, meaning my app directory is huge.

Why's that? Here's another handy command to see that:
du --max-depth 1

This shows me size of directories inside my project:

8   ./.bundle
108 ./test
824388  ./.ruby-lsp
176 ./config
554380  ./vendor
56  ./lib
108 ./db
125260  ./tmp
28  ./bin
3034272 ./old
24  ./.kamal
39508   ./.git
8   ./.vscode
2300452 ./storage
232 ./spec
8216    ./app
20  ./public
427176  ./log
4614096 .
Enter fullscreen mode Exit fullscreen mode

The thing is, I started deploying from my laptop, so I have all this files that are not present when deploying from CI/CD (as would be optimal way).

So, we have /storage (with all files), /.ruby-lsp (from editor), /old (thats just some migration data), and /vendor.

I don't want these in my image!
Let's not include them:

# .dockerignore

old/
*.log

vendor/
tmp/
.ruby-lsp/
Enter fullscreen mode Exit fullscreen mode

After another build, I check my image again, and I got to much nicer ~600MB image.

There might be still some room for improvement, but I'm quite happy with this - my build time is down significantly, my self-hosted registry is not full of useless data, and I learned something new.

💖 💪 🙅 🚩
janmpeterka
Jan Peterka

Posted on March 14, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related