From 83f9d4d062a0fe92be8a3d7ce8b1b564076529e3 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 20 Mar 2026 20:19:11 -0700 Subject: [PATCH 01/16] Optimize Docker configuration with slim image, .dockerignore, and custom entrypoint This drastically reduces the build upload payload size from 800MB to ~1MB by ignoring node_modules, and reduces the final image size using the slim ruby debian variant. Also cleanly handles server.pid cleanup via a robust entrypoint.sh. --- .dockerignore | 10 ++++++++-- Dockerfile | 5 ++++- docker-compose.yml | 2 +- docker-entrypoint.sh | 8 ++++++++ 4 files changed, 21 insertions(+), 4 deletions(-) create mode 100755 docker-entrypoint.sh diff --git a/.dockerignore b/.dockerignore index c008cb669..9391c9910 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,5 @@ # Don't include any of these files in the Dockerfile build context. # This will improve build performance and reduce the final image size. -# See: https://docs.docker.com/engine/reference/builder/#dockerignore-file .git/ .github/ .dockerignore @@ -9,5 +8,12 @@ .rubocop.yml CHANGELOG.md docker-compose.yml -log/ +node_modules/ +log/* +tmp/* +!log/.keep +!tmp/.keep +storage/* +public/assets +public/packs test/ diff --git a/Dockerfile b/Dockerfile index 4f0ff7f0c..ec5e98bbc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # The image to build from. -FROM ruby:3.2.1 +FROM ruby:3.2.1-slim # Properties/labels for the image. LABEL maintainer="Notebook.ai Contributors" @@ -43,5 +43,8 @@ EXPOSE 3000/tcp # Run unprivileged USER notebookai +# Configure the main process to run when running the image +ENTRYPOINT ["./docker-entrypoint.sh"] + # Start the server using Puma! CMD ["bundle", "exec", "puma", "-C", "config/puma.rb", "-e", "development", "-b", "tcp://0.0.0.0:3000"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index f3aae01c5..873851f9b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,6 +4,6 @@ services: build: . ports: - "3000:3000/tcp" - command: sh -c "rake db:migrate && rm -f tmp/pids/server.pid && exec rails server -b 0.0.0.0" + command: sh -c "rake db:migrate && exec rails server -b 0.0.0.0" volumes: - "./:/home/notebookai" diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 000000000..9806d66b2 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -e + +# Remove a potentially pre-existing server.pid for Rails. +rm -f /home/notebookai/tmp/pids/server.pid + +# Then exec the container's main process (what's set as CMD in the Dockerfile). +exec "$@" From 4043c251c9d4783971a8d4acc0a4070534c09114 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 20 Mar 2026 20:31:40 -0700 Subject: [PATCH 02/16] Allow Railway PR URLs for host authorization --- config/environments/production.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/environments/production.rb b/config/environments/production.rb index 1d50c10f9..a4f42a7aa 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,6 +1,10 @@ Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. + # Allow PR environments on Railway (auto-deployed dynamic domains) + config.hosts << /.*\.up\.railway\.app/ + config.hosts << ENV["RAILWAY_PUBLIC_DOMAIN"] if ENV["RAILWAY_PUBLIC_DOMAIN"].present? + # Code is not reloaded between requests. config.cache_classes = true From 7b781c1e80832d36af3be44d057d0c3c3c0ffb00 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 20 Mar 2026 20:38:13 -0700 Subject: [PATCH 03/16] Add railway.toml for PR database migrations --- railway.toml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 railway.toml diff --git a/railway.toml b/railway.toml new file mode 100644 index 000000000..01e8e0a9f --- /dev/null +++ b/railway.toml @@ -0,0 +1,2 @@ +[deploy] +preDeployCommand = "bundle exec rails db:prepare" From bf00695274059913c9cfe29d659a77ed2ba83e62 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 20 Mar 2026 21:51:12 -0700 Subject: [PATCH 04/16] Fix Dockerfile npm installation --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index ec5e98bbc..3387e0241 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,9 +13,9 @@ RUN groupadd --system --gid 1000 notebookai && \ useradd --system --home-dir /home/notebookai --gid notebookai --uid 1000 --shell /bin/bash notebookai # Install system dependencies -RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash - && \ +RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \ apt-get update -qq && \ - apt-get install -y build-essential libpq-dev nodejs imagemagick libmagickwand-dev curl && \ + apt-get install -y build-essential libpq-dev nodejs npm imagemagick libmagickwand-dev curl && \ rm --recursive --force /var/lib/apt/lists/* # Install yarn via npm (avoids the deprecated apt-key approach) From 71df0f7c9f574f75e89f5a802f2640b063577d56 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 20 Mar 2026 21:53:29 -0700 Subject: [PATCH 05/16] Pin Node to 16.20.2 via direct tarball download --- Dockerfile | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3387e0241..587cdc56c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,14 +12,18 @@ ENV RAILS_ENV=${RAILS_ENV} RUN groupadd --system --gid 1000 notebookai && \ useradd --system --home-dir /home/notebookai --gid notebookai --uid 1000 --shell /bin/bash notebookai +# Install Node.js 16.x explicitly (NodeSource dropped support for it) +RUN curl -fsSLO https://nodejs.org/dist/v16.20.2/node-v16.20.2-linux-x64.tar.gz && \ + tar -xzf node-v16.20.2-linux-x64.tar.gz -C /usr/local --strip-components=1 && \ + rm node-v16.20.2-linux-x64.tar.gz + # Install system dependencies -RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \ - apt-get update -qq && \ - apt-get install -y build-essential libpq-dev nodejs npm imagemagick libmagickwand-dev curl && \ +RUN apt-get update -qq && \ + apt-get install -y build-essential libpq-dev imagemagick libmagickwand-dev curl && \ rm --recursive --force /var/lib/apt/lists/* -# Install yarn via npm (avoids the deprecated apt-key approach) -RUN npm install -g yarn +# Install yarn via npm (matches local tools specification) +RUN npm install -g yarn@1.22.22 # Set the notebookai user's home directory as the working directory. WORKDIR /home/notebookai From 9871fe156f34333bc1c3abaa2105e8c7aa34ce9f Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 20 Mar 2026 21:58:39 -0700 Subject: [PATCH 06/16] Install curl before downloading Node --- Dockerfile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 587cdc56c..486cc3540 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,16 +12,16 @@ ENV RAILS_ENV=${RAILS_ENV} RUN groupadd --system --gid 1000 notebookai && \ useradd --system --home-dir /home/notebookai --gid notebookai --uid 1000 --shell /bin/bash notebookai +# Install system dependencies (including curl which is needed for Node download) +RUN apt-get update -qq && \ + apt-get install -y build-essential libpq-dev imagemagick libmagickwand-dev curl && \ + rm --recursive --force /var/lib/apt/lists/* + # Install Node.js 16.x explicitly (NodeSource dropped support for it) RUN curl -fsSLO https://nodejs.org/dist/v16.20.2/node-v16.20.2-linux-x64.tar.gz && \ tar -xzf node-v16.20.2-linux-x64.tar.gz -C /usr/local --strip-components=1 && \ rm node-v16.20.2-linux-x64.tar.gz -# Install system dependencies -RUN apt-get update -qq && \ - apt-get install -y build-essential libpq-dev imagemagick libmagickwand-dev curl && \ - rm --recursive --force /var/lib/apt/lists/* - # Install yarn via npm (matches local tools specification) RUN npm install -g yarn@1.22.22 From 4f58d8595f6b09ffde32f2be3a10ff4f37bfe96f Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 20 Mar 2026 22:03:10 -0700 Subject: [PATCH 07/16] Install git in Dockerfile for bundler github dependencies --- Dockerfile | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 486cc3540..7e0b6ecf2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,15 +12,21 @@ ENV RAILS_ENV=${RAILS_ENV} RUN groupadd --system --gid 1000 notebookai && \ useradd --system --home-dir /home/notebookai --gid notebookai --uid 1000 --shell /bin/bash notebookai -# Install system dependencies (including curl which is needed for Node download) +# Install system dependencies (including curl which is needed for Node download, and git for Bundler) RUN apt-get update -qq && \ - apt-get install -y build-essential libpq-dev imagemagick libmagickwand-dev curl && \ + apt-get install -y build-essential libpq-dev imagemagick libmagickwand-dev curl git && \ rm --recursive --force /var/lib/apt/lists/* -# Install Node.js 16.x explicitly (NodeSource dropped support for it) -RUN curl -fsSLO https://nodejs.org/dist/v16.20.2/node-v16.20.2-linux-x64.tar.gz && \ - tar -xzf node-v16.20.2-linux-x64.tar.gz -C /usr/local --strip-components=1 && \ - rm node-v16.20.2-linux-x64.tar.gz +# Install Node.js 16.x explicitly and support both ARM and x64 architectures +RUN ARCH= && dpkgArch="$(dpkg --print-architecture)" && \ + case "${dpkgArch##*-}" in \ + amd64) ARCH='x64';; \ + arm64) ARCH='arm64';; \ + *) echo "unsupported architecture"; exit 1 ;; \ + esac && \ + curl -fsSLO "https://nodejs.org/dist/v16.20.2/node-v16.20.2-linux-$ARCH.tar.gz" && \ + tar -xzf "node-v16.20.2-linux-$ARCH.tar.gz" -C /usr/local --strip-components=1 && \ + rm "node-v16.20.2-linux-$ARCH.tar.gz" # Install yarn via npm (matches local tools specification) RUN npm install -g yarn@1.22.22 From c8c7d108eb9770ede223f2140c108cc95ed1a76c Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 20 Mar 2026 22:20:24 -0700 Subject: [PATCH 08/16] Allow Railway hostnames in development mode --- config/environments/development.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/environments/development.rb b/config/environments/development.rb index 121334cb7..a24114ecf 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,6 +1,10 @@ Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. + # Allow PR environments on Railway (auto-deployed dynamic domains) + config.hosts << /.*\.up\.railway\.app/ + config.hosts << ENV["RAILWAY_PUBLIC_DOMAIN"] if ENV["RAILWAY_PUBLIC_DOMAIN"].present? + # In the development environment your application's code is reloaded on # every request. This slows down response time but is perfect for development # since you don't have to restart the web server when you make code changes. From c7ef08afcb7f5864bcc5dbc788a22fc32ea1c200 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 20 Mar 2026 22:38:11 -0700 Subject: [PATCH 09/16] Move db:prepare to runtime entrypoint so SQLite persists --- docker-entrypoint.sh | 3 +++ railway.toml | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) delete mode 100644 railway.toml diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 9806d66b2..6194cbc2a 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -4,5 +4,8 @@ set -e # Remove a potentially pre-existing server.pid for Rails. rm -f /home/notebookai/tmp/pids/server.pid +# Ensure the database is prepared (migrated & seeded) before starting the server +bundle exec rails db:prepare + # Then exec the container's main process (what's set as CMD in the Dockerfile). exec "$@" diff --git a/railway.toml b/railway.toml deleted file mode 100644 index 01e8e0a9f..000000000 --- a/railway.toml +++ /dev/null @@ -1,2 +0,0 @@ -[deploy] -preDeployCommand = "bundle exec rails db:prepare" From 27d20e2f2a0bed1658102681e7d245b2d6a4cd5a Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 20 Mar 2026 22:52:37 -0700 Subject: [PATCH 10/16] Disable Puma workers in development mode to save memory --- config/puma.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/puma.rb b/config/puma.rb index 04bf11878..b88135a9a 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -23,7 +23,8 @@ # Workers do not work on JRuby or Windows (both of which do not support # processes). # -workers ENV.fetch("WEB_CONCURRENCY") { 2 } +# In development, default to 0 workers (single mode) to save memory and allow for easier debugging. +workers ENV.fetch("WEB_CONCURRENCY") { ENV.fetch("RAILS_ENV", "development") == "development" ? 0 : 2 } # Use the `preload_app!` method when specifying a `workers` number. # This directive tells Puma to first boot the application and load code From 4bb9c319ec2453adf2370b873db0af552929d3b2 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 20 Mar 2026 22:54:40 -0700 Subject: [PATCH 11/16] Precompile assets during Docker build to save runtime memory --- Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile b/Dockerfile index 7e0b6ecf2..48a57d111 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,6 +47,9 @@ COPY . . # Adjust permissions on all copied files to match the system user RUN chown -R notebookai:notebookai /home/notebookai +# Precompile assets during docker build to prevent OOM memory spikes at runtime +RUN SECRET_KEY_BASE=dummy bundle exec rake assets:precompile + # This image should expose port 3000. EXPOSE 3000/tcp From 142077e2da9d89983b759f40f15e5823d2a5e6b6 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 20 Mar 2026 23:04:02 -0700 Subject: [PATCH 12/16] Disable eager loading in development mode --- config/environments/development.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/environments/development.rb b/config/environments/development.rb index a24114ecf..13970ebc9 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -11,7 +11,8 @@ config.cache_classes = false # Do not eager load code on boot. - config.eager_load = true + # We set this to false to save hundreds of megabytes of memory on boot. + config.eager_load = false # Show full error reports. config.consider_all_requests_local = true From 2ec60fee9d700c6708ad01d120ee17aa63ab3cf5 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 20 Mar 2026 23:38:07 -0700 Subject: [PATCH 13/16] Install jemalloc to reduce Ruby memory fragmentation and prevent OOMs --- Dockerfile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 48a57d111..530dfb31f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ RUN groupadd --system --gid 1000 notebookai && \ # Install system dependencies (including curl which is needed for Node download, and git for Bundler) RUN apt-get update -qq && \ - apt-get install -y build-essential libpq-dev imagemagick libmagickwand-dev curl git && \ + apt-get install -y build-essential libpq-dev imagemagick libmagickwand-dev curl git libjemalloc2 && \ rm --recursive --force /var/lib/apt/lists/* # Install Node.js 16.x explicitly and support both ARM and x64 architectures @@ -56,6 +56,9 @@ EXPOSE 3000/tcp # Run unprivileged USER notebookai +# Enable jemalloc to drastically reduce memory fragmentation and usage +ENV LD_PRELOAD="libjemalloc.so.2" + # Configure the main process to run when running the image ENTRYPOINT ["./docker-entrypoint.sh"] From 804ddcd66e5e3f97d0e20517125ef600510fbadf Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 20 Mar 2026 23:48:59 -0700 Subject: [PATCH 14/16] Cap Node.js memory during asset precompilation to prevent Builder OOM --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 530dfb31f..344cbb444 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,7 +48,8 @@ COPY . . RUN chown -R notebookai:notebookai /home/notebookai # Precompile assets during docker build to prevent OOM memory spikes at runtime -RUN SECRET_KEY_BASE=dummy bundle exec rake assets:precompile +# We strictly limit Node.js memory to 1GB to prevent Railway's Builder container from OOMing +RUN NODE_OPTIONS="--max_old_space_size=1024" SECRET_KEY_BASE=dummy bundle exec rake assets:precompile # This image should expose port 3000. EXPOSE 3000/tcp From 1f69b09d525fdf35af774d06b159575ec9737cb8 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Sat, 21 Mar 2026 00:01:09 -0700 Subject: [PATCH 15/16] Run precompile as unprivileged user to prevent log permission errors at runtime --- Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 344cbb444..80f0a7c5c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,6 +47,9 @@ COPY . . # Adjust permissions on all copied files to match the system user RUN chown -R notebookai:notebookai /home/notebookai +# Drop down to the unprivileged user before running Rake, so any files it generates (like logs) are owned correctly +USER notebookai + # Precompile assets during docker build to prevent OOM memory spikes at runtime # We strictly limit Node.js memory to 1GB to prevent Railway's Builder container from OOMing RUN NODE_OPTIONS="--max_old_space_size=1024" SECRET_KEY_BASE=dummy bundle exec rake assets:precompile @@ -54,9 +57,6 @@ RUN NODE_OPTIONS="--max_old_space_size=1024" SECRET_KEY_BASE=dummy bundle exec r # This image should expose port 3000. EXPOSE 3000/tcp -# Run unprivileged -USER notebookai - # Enable jemalloc to drastically reduce memory fragmentation and usage ENV LD_PRELOAD="libjemalloc.so.2" From be3089c6db3fe7e9e81550f6e1df186015e0ef1d Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Sat, 21 Mar 2026 00:13:58 -0700 Subject: [PATCH 16/16] Explicitly run db:seed on container boot to guarantee test user creation --- docker-entrypoint.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 6194cbc2a..157efad84 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -6,6 +6,7 @@ rm -f /home/notebookai/tmp/pids/server.pid # Ensure the database is prepared (migrated & seeded) before starting the server bundle exec rails db:prepare +bundle exec rails db:seed # Then exec the container's main process (what's set as CMD in the Dockerfile). exec "$@"