How can I minimize the discourse Docker image size?

Hi All,
We had to build a discourse image for deployment in our K8s cluster, which has some restrictions like no open internet access etc. The forum is meant to be internal, so no issues there. To acheive this, I followed the official supported install method, and created a Dockerfile based on the launcher script’s bootstrap and run commands, where we do all the steps which doesn’t require a database in the build time and all the remaining steps on bootup. It is kinda slow to boot, but it is manageable for now.

One problem with this is the final image is pretty huge ( ~2GB ), since I use the discourse base image as it is to run the launcher script on top of. I went through the base image’s Dockerfile here - discourse_docker/image/base/Dockerfile at master · discourse/discourse_docker · GitHub . It looks like a lot of stuff like postgres and redis are being installed. We are trying to use a multi-container approach, where the discourse, redis and postgres are in different containers. I understand that this meant to be a common base image for all the flavours of installations that the supported install supports. But I am looking for a way to trim down the fat in the final image. In addition to this, removing unnecessary stuff would be better in terms of vulnerabilities.

My question is, can i safely remove the postgres, redis dependencies that are being installed in the base docker image, without breaking something in the discourse application ? Also is it possible to remove stuff like curl, wget from the final image too, as those too will rarely be needed in our setup, since the console will be barely accessible anyway.

1 Like

You can - I also have an open initiative to slim the image by doing exactly that here:

RE: slow to boot images – we’re currently working on something for that too.

3 Likes

Is this a good place to ask, what are the “slim” builds and can they be made use of by self-hosters?

Where a normal build is presently about 1G, the slim builds are about 600M. (Looking at this index of docker images)

(ask.discourse.com reckons these slim builds are useful for just these kinds of purposes, but I don’t know if the information is good.)

2 Likes

I tried replacing the normal base image with the slim base image, and it starts to fail. I see a lot of different types of errors, so i am not sure what is causing them.

Error set 1

/usr/local/lib/ruby/gems/3.3.0/gems/bundler-2.6.4/lib/bundler/definition.rb:683:in `materialize’: Could not find bootsnap-1.18.6, actionmailer-8.0.2, actionpack-8.0.2, actionview-8.0.2, activemodel-8.0.2, activerecord-8.0.2, activesupport-8.0.2, railties-8.0.2, propshaft-1.2.1, json-2.13.0, actionview_precompiler-0.4.0, discourse-seed-fu-2.3.12, mail-2.8.1, mini_mime-1.1.5, mini_suffix-0.3.3, redis-5.4.0, redis-namespace-1.11.0, active_model_serializers-0.8.4, http_accept_language-2.1.1, discourse-fonts-0.0.19, discourse-emojis-1.0.41, message_bus-4.4.1, rails_multisite-7.0.0, fastimage-2.3.1, aws-sdk-s3-1.182.0, aws-sdk-sns-1.96.0, excon-1.2.5, unf-0.2.0, email_reply_trimmer-0.2.0, image_optim-0.31.4, multi_json-1.17.0, mustache-1.1.1, nokogiri-1.18.8-x86_64-linux-gnu, loofah-2.24.1, css_parser-1.21.1, omniauth-2.1.2, omniauth-facebook-9.0.0, omniauth-twitter-1.4.0, omniauth-github-2.0.0, omniauth-oauth2-1.7.3, omniauth-google-oauth2-1.0.1, oj-3.16.11, pg-1.5.9, mini_sql-1.6.0, pry-rails-0.3.11, pry-byebug-3.11.0, rtlcss-0.2.1, messageformat-wrapper-1.1.0, rake-13.3.0, thor-1.4.0, diffy-3.4.4, rinku-2.0.6, sidekiq-7.3.9, mini_scheduler-0.18.0, execjs-2.10.0, mini_racer-0.19.0, highline-3.1.2, rack-2.2.17, rack-protection-3.2.0, cbor-0.5.9.8, cose-1.3.1, addressable-2.8.7, json_schemer-2.4.0, net-imap-0.5.9, digest-3.2.0, capybara-3.40.0, webmock-3.25.1, simplecov-0.22.0, test-prof-1.4.4, rails-dom-testing-2.3.0, minio_runner-1.0.0, capybara-playwright-driver-0.5.6, rspec-3.13.1, listen-3.9.0, certified-1.0.0, fabrication-3.0.0, mocha-2.7.1, rb-fsevent-0.11.2, rspec-rails-8.0.1, shoulda-matchers-6.5.0, rspec-html-matchers-0.10.0, pry-stack_explorer-0.6.1, byebug-12.0.0, rubocop-discourse-3.12.1, parallel_tests-5.3.0, rswag-specs-2.16.0, annotaterb-4.17.0, syntax_tree-6.3.0, rspec-multi-mock-0.3.1, ruby-prof-1.7.2, bullet-8.0.8, better_errors-2.10.1, binding_of_caller-1.0.1, yaml-lint-0.1.2, yard-0.9.37, discourse_dev_assets-0.0.5, faker-3.5.2, fast_blank-1.0.1, lru_redux-1.1.0, htmlentities-4.3.4, rack-mini-profiler-4.0.0, unicorn-6.1.0, puma-6.6.0, rbtrace-0.5.2, ruby-readability-0.7.2, stackprof-0.2.27, memory_profiler-1.1.0, cppjieba_rb-0.4.4, lograge-0.14.0, logstash-event-1.2.02, logster-2.20.1, sassc-embedded-1.80.2, rotp-6.3.0, rqrcode-3.1.0, rubyzip-2.4.1, sshkey-3.0.0, rchardet-1.9.0, lz4-ruby-0.3.3, sanitize-7.0.0, web-push-3.0.1, colored2-4.0.3, maxminddb-0.1.22, rails_failover-2.3.0, faraday-2.13.3, faraday-retry-2.3.2, net-http-0.6.0, cgi-0.5.0, tzinfo-data-1.2025.2, csv-3.3.5, iso8601-0.13.0, rrule-0.6.0, dry-initializer-3.2.0, parallel-1.27.0, inflection-1.0.0, multipart-post-2.4.1, faraday-multipart-1.1.1, zendesk_api-1.38.0.rc1, stripe-11.1.0, sawyer-0.9.2, octokit-5.6.1, tokenizers-0.5.5-x86_64-linux, tokenizers-0.5.5, tiktoken_ruby-0.0.11.1-x86_64-linux, discourse_ai-tokenizers-0.3.1, ed25519-1.4.0, Ascii85-2.0.1, ruby-rc4-0.1.5, hashery-2.1.2, ttfunk-1.8.0, afm-0.2.2, pdf-reader-2.14.1, msgpack-1.8.0, activejob-8.0.2, rack-session-1.0.2, rack-test-2.2.0, rails-html-sanitizer-1.6.2, useragent-0.16.11, builder-3.3.0, erubi-1.13.1, timeout-0.4.3, base64-0.3.0, benchmark-0.4.1, bigdecimal-3.2.2, concurrent-ruby-1.3.5, connection_pool-2.5.3, drb-2.2.3, i18n-1.14.7, logger-1.7.0, minitest-5.25.5, securerandom-0.4.1, tzinfo-2.0.6, uri-1.0.3, irb-1.15.2, rackup-1.0.1, zeitwerk-2.7.3, ffi-1.17.2-x86_64-linux-gnu, redis-client-0.25.1, aws-sdk-core-3.226.0, aws-sdk-kms-1.99.0, aws-sigv4-1.12.1, exifr-1.4.1, fspath-3.1.2, image_size-3.4.0, in_threads-1.6.0, progress-3.6.0, racc-1.8.1, crass-1.0.6, hashie-5.0.0, omniauth-oauth-1.2.1, oauth2-1.4.11, jwt-2.10.1, ostruct-0.6.3, pry-0.15.2, libv8-node-24.1.0.0-x86_64-linux, libv8-node-24.1.0.0, reline-0.6.2, openssl-signature_algorithm-1.3.0, public_suffix-6.0.2, hana-1.3.7, regexp_parser-2.10.0, simpleidn-0.2.3, date-3.4.1, matrix-0.4.3, xpath-3.2.0, crack-1.0.0, hashdiff-1.2.0, docile-1.4.1, simplecov-html-0.13.2, simplecov_json_formatter-0.1.4, playwright-ruby-client-1.52.0, rspec-core-3.13.5, rspec-expectations-3.13.5, rspec-mocks-3.13.5, rb-inotify-0.11.1, rspec-support-3.13.4, lint_roller-1.1.0, rubocop-1.78.0, rubocop-capybara-2.22.1, rubocop-factory_bot-2.27.1, rubocop-rails-2.32.0, rubocop-rspec-3.6.0, rubocop-rspec_rails-2.31.0, json-schema-5.2.1, prettier_print-1.2.1, uniform_notifier-1.17.0, rouge-4.6.0, debug_inspector-1.2.0, literate_randomizer-0.4.0, kgio-2.11.4, raindrops-0.20.1, nio4r-2.7.4, optimist-3.2.1, guess_html_encoding-0.0.11, rexml-3.4.1, request_store-1.7.0, sass-embedded-1.85.0-x86_64-linux-gnu, chunky_png-1.4.0, rqrcode_core-2.0.0, openssl-3.3.0, faraday-net_http-3.4.1, globalid-1.2.1, pp-0.6.2, rdoc-6.14.2, webrick-1.9.1, aws-eventstream-1.4.0, aws-partitions-1.1117.0, jmespath-1.6.2, oauth-1.1.0, multi_xml-0.7.2, coderay-1.1.3, method_source-1.1.0, io-console-0.8.1, mime-types-3.7.0, diff-lcs-1.6.2, language_server-protocol-3.17.0.5, parser-3.3.8.0, rainbow-3.1.1, rubocop-ast-1.46.0, ruby-progressbar-1.13.0, unicode-display_width-3.1.4, google-protobuf-4.31.1-x86_64-linux-gnu, google-protobuf-4.31.1, erb-5.0.2, psych-5.2.6, oauth-tty-1.0.5, snaky_hash-2.0.3, version_gem-1.1.8, mime-types-data-3.2025.0715, ast-2.4.3, prism-1.4.0, unicode-emoji-4.0.4, stringio-3.1.7 in locally installed gems (Bundler::GemNotFound)

and

supervisor pid: 577 unicorn pid: 583
./config/unicorn_launcher: line 66: unicorn: command not found

EDIT: I apologize for the trouble. These failures might just be because of the build time failures. From some reason, the build looked like it completed normally. But now that I see, I can see that some steps failed. especially cd /var/www/discourse && su discourse -c 'bundle install --jobs $(($(nproc) - 1)) --retry 3', due to a permission issue. Although i wonder, why did this change between the slim and the normal base image

Error during build step

Retrying download gem from https://rubygems.org/ due to error (2/4): Bundler::PermissionError There was an error while trying to write to `/usr/local/lib/ruby/gems/3.3.0/cache/rake-13.3.0.gem`. It is likely that you need to grant write permissions for that path.

The slim image still include postgres and redis.

The difference between slim and the other starts from command 37. The following commands are not executed in the slim:

ENV RAILS_ENV=production
RUN |3 DEBIAN_RELEASE=bookworm PG_MAJOR=15 DISCOURSE_BRANCH=main /bin/sh -c cd /var/www/discourse &&    sudo -u discourse bundle config --local deployment true &&    sudo -u discourse bundle config --local path ./vendor/bundle &&    sudo -u discourse bundle config --local without test development &&    sudo -u discourse bundle install --jobs $(($(nproc) - 1)) &&    find /var/www/discourse/vendor/bundle -name cache -not -path '*/gems/*' -type d -exec rm -rf {} + &&    find /var/www/discourse/vendor/bundle -name tmp -type d -exec rm -rf {} + # buildkit
RUN |3 DEBIAN_RELEASE=bookworm PG_MAJOR=15 DISCOURSE_BRANCH=main /bin/sh -c cd /var/www/discourse &&    sudo -u discourse /bin/bash -c 'if [ -f yarn.lock ]; then yarn install --frozen-lockfile && yarn cache clean; else pnpm install --frozen-lockfile; fi' # buildkit

Basically, it is missing some build steps. No bundle install no pnpm install. Stuff you will eventually need.

The big pains are in command 11: 390MiB

RUN |2 DEBIAN_RELEASE=bookworm PG_MAJOR=15 /bin/sh -c echo "debconf debconf/frontend select Teletype" | debconf-set-selections;     apt-get -y update && DEBIAN_FRONTEND=noninteractive apt-get -y install gnupg sudo curl fping locales     ca-certificates rsync     cmake g++ pkg-config patch     libxslt-dev libcurl4-openssl-dev     libssl-dev libyaml-dev libtool     libpcre3 libpcre3-dev zlib1g zlib1g-dev     libxml2-dev gawk parallel     libreadline-dev anacron wget     psmisc whois brotli libunwind-dev     libtcmalloc-minimal4 cmake     pngcrush pngquant ripgrep poppler-utils     ghostscript libjbig0 libtiff6 libpng16-16 libfontconfig1     libwebpdemux2 libwebpmux3 libxext6 librsvg2-2 libgomp1     fonts-urw-base35 libheif1/${DEBIAN_RELEASE}-backports     nginx-common &&     DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends git rsyslog logrotate cron ssh-client less;     install -d /usr/share/postgresql-common/pgdg &&    curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc &&    echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt ${DEBIAN_RELEASE}-pgdg main" > /etc/apt/sources.list.d/pgdg.list;     curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -;     echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list;     curl --silent --location https://deb.nodesource.com/setup_22.x | sudo bash -;     sed -i -e 's/start -q anacron/anacron -s/' /etc/cron.d/anacron;     sed -i.bak 's/$ModLoad imklog/#$ModLoad imklog/' /etc/rsyslog.conf;     sed -i.bak 's/module(load="imklog")/#module(load="imklog")/' /etc/rsyslog.conf;     dpkg-divert --local --rename --add /sbin/initctl;     sh -c "test -f /sbin/initctl || ln -s /bin/true /sbin/initctl";     apt-get -y update && DEBIAN_FRONTEND=noninteractive apt-get -y install runit socat     libpq-dev postgresql-client-${PG_MAJOR}     postgresql-${PG_MAJOR} postgresql-contrib-${PG_MAJOR} postgresql-${PG_MAJOR}-pgvector     nodejs yarn &&    mkdir -p /etc/runit/1.d # buildkit

It installs a lot of required tooling for other parts, also also postgresql:

gnupg sudo curl fping locales ca-certificates rsync cmake g++ pkg-config patch libxslt-dev libcurl4-openssl-dev libssl-dev libyaml-dev libtool libpcre3 libpcre3-dev zlib1g zlib1g-dev libxml2-dev gawk parallel libreadline-dev anacron wget psmisc whois brotli libunwind-dev libtcmalloc-minimal4 cmake pngcrush pngquant ripgrep poppler-utils ghostscript libjbig0 libtiff6 libpng16-16 libfontconfig1 libwebpdemux2 libwebpmux3 libxext6 librsvg2-2 libgomp1 fonts-urw-base35 libheif1 nginx-common git rsyslog logrotate cron ssh-client less runit socat libpq-dev postgresql-client postgresql postgresql-contrib postgresql-pgvector nodejs yarn

1 Like

Aside from postgres+redis (which I have a proposal to provide above), some of the larger libraries installed there are for compiling gems that Discourse uses. There is currently no good and simple way to remove those libraries right now.

Followup, the above proposal has been merged, and *-web-only images are now being built, for images that do not include postgres or redis. (latest built tag at time of writing is discourse/base:2.0.20250813-2244-web-only)