Ronnie Miller

My name is Ronnie. I'm a product-minded software engineer, building SaaS products and the infrastructure that runs them.

I live on California's Redwood Coast and have been building software for over 15 years. That work covers code, cloud infrastructure, security, and leading engineering teams.

Currently, you can find me leading engineering efforts as Partner and CTO of KABI Automotive. I also explore the edges of AI at BuildTestRun and share what I'm building and learning on my YouTube channel, Build at Scale. The notes below are where I write down what I build and what breaks.

A time on a date column moved the month

The September calendar in my invoicing app showed October 1 entries on September 1. A month range built from a zone-aware time was compared to a date column in UTC. The audit after it found Time.zone leaking between requests.

ECS stopped tasks that were still booting

Deploys failed at random, and ECS stopped tasks that were about to pass their health checks. The health check grace period was 15 seconds, and a task needed about 70 seconds to boot.

Two places MySQL wants another layer

A UNION of queries with LIMIT failed with a syntax error, and a DELETE that read its own table failed with ERROR 1093. MySQL wanted parentheses around the first and a derived table inside the second.

CONVERT TO runs after MODIFY

An emoji from speech-to-text broke a job that saved captions to a utf8mb3 table. Converting to utf8mb4 turned every TEXT column into MEDIUMTEXT, even with a MODIFY in the same ALTER.

chruby picked the broken Ruby

ruby-install said Ruby 3.4.10 installed fine, but chruby 3.4.10 selected a broken x86_64 copy I had renamed and left in ~/.rubies. chruby keeps the last partial match.

Three ways Puma didn't restart

A systemd path unit was supposed to restart Puma after each Capistrano deploy. It never ran, then it caused eight seconds of 502s per deploy, and then a bad config line took the site down two days later.

Ruby and Rails 28

  1. A time on a date column moved the month
  2. CONVERT TO runs after MODIFY
  3. chruby picked the broken Ruby
  4. Three ways Puma didn't restart
  5. Notes from Rails 6.1 to 8.1 in a day
  6. Where the email link 404s came from
  7. A new cookie domain needs a new cookie name
  8. A hash without braces became keyword arguments
  9. driven_by replaced my Chrome driver
  10. The relation ran after the block ended
  11. Two Ruby managers, one gem directory
  12. Setting config.hosts dropped the defaults
  13. Time.local doesn't know the Rails zone
  14. Capybara waited for a request my test never checked
  15. nginx returned 405 for PATCH
  16. Capistrano couldn't authenticate, but ssh could
  17. A Compose volume was hiding my Bundler config
  18. A missing index, found with rack-mini-profiler
  19. wkhtmltopdf in an Alpine image
  20. after_commit and read_changed? never fire together
  21. Connecting Rails to Amazon RDS with SSL
  22. Running Rails migrations on an ECS deploy
  23. Ruby 3.3: the lambda method requires a literal block
  24. Is this memoization thread safe?
  25. Building Ruby 3.0.7 on an M2 Mac
  26. Ruby 2.3 – A Gift for You and Me
  27. RailsConf 2015, Atlanta Georgia
  28. Trying out new features of Ruby 2.2

Command line and Git 21

  1. chruby picked the broken Ruby
  2. Three ways Puma didn't restart
  3. Wrangler used a token I forgot about
  4. My cd hook was filling Claude's context
  5. localhost was IPv6 on one end
  6. Two Ruby managers, one gem directory
  7. export with no arguments prints everything
  8. find -path "./node_modules/*" only matches the top level
  9. A guard hook that errors lets the command run
  10. Capistrano couldn't authenticate, but ssh could
  11. Moving Neovim to vim.lsp.enable
  12. tmux was running as x86 on my M4
  13. Git doesn't know where a branch came from
  14. Print only the last block from a log
  15. In a rebase, ours and theirs swap places
  16. From a log line to a command on the same box
  17. An alias can't put arguments in the middle
  18. fatal: bad revision 'main' in a GitHub Action
  19. Dear xargs, I appreciate you
  20. Migrate a Git Repository between Local Machines
  21. Vim plugins lost in the Vundle

AWS 20

  1. ECS stopped tasks that were still booting
  2. ECS Exec is not a shell
  3. Logs Insights months start in UTC
  4. Counting requests per 15 minutes in Logs Insights
  5. ECS Exec always runs as root
  6. Docker layer caching in CI, and the tag I overwrote
  7. SSM stopped working when I removed the public IP
  8. pulumi import gave me stale code
  9. TLS from the load balancer to the container
  10. Connecting Rails to Amazon RDS with SSL
  11. Running Rails migrations on an ECS deploy
  12. You can't backdate an S3 object
  13. Inspector suppression rules update by ARN, not by name
  14. Packer's env() only works in a variable default
  15. Mount the partition, not the disk
  16. Block Public Access wins over a public bucket policy
  17. From a log line to a command on the same box
  18. Remote Database Connection via ECS Fargate
  19. HTTPS For Your Static S3 Website
  20. Static Website Hosting on Amazon S3

Docker and CI 19

  1. ECS stopped tasks that were still booting
  2. compose run used the new image, the server didn't
  3. Notes from Rails 6.1 to 8.1 in a day
  4. A frontend deploy broke LLM streaming
  5. ECS Exec is not a shell
  6. driven_by replaced my Chrome driver
  7. WARP blamed 1Password, but Docker was in the way
  8. Setting config.hosts dropped the defaults
  9. Time.local doesn't know the Rails zone
  10. setuptools 82 removed pkg_resources
  11. Making the Claude GitHub Action open the PR
  12. There is no author_association on sender
  13. Deploying a Cloudflare Worker from GitHub Actions
  14. Postgres in Docker ignored my POSTGRES_ settings
  15. ECS Exec always runs as root
  16. A Compose volume was hiding my Bundler config
  17. Cloud Run job args replace the command
  18. Docker layer caching in CI, and the tag I overwrote
  19. wkhtmltopdf in an Alpine image

JavaScript 15

  1. Where the email link 404s came from
  2. The caller talked during a tool call
  3. A frontend deploy broke LLM streaming
  4. localhost was IPv6 on one end
  5. One API route on a static Astro site
  6. Claude Desktop started my MCP server with Node 10
  7. Hanging up is the hard part
  8. A subagent's model is not the main model
  9. Tailwind didn't scan my UI package
  10. An old PostCSS config broke the Astro build
  11. grammY's bot.start() never returns
  12. Deploying a Cloudflare Worker from GitHub Actions
  13. Bootstrap 2 caps you at jQuery 2
  14. An axios upgrade dropped my POST body
  15. A date dropdown that broke two ways

AI and agents 12

  1. The caller talked during a tool call
  2. A frontend deploy broke LLM streaming
  3. My cd hook was filling Claude's context
  4. Claude Code kept dropping on my home Wi-Fi
  5. Claude Desktop started my MCP server with Node 10
  6. export with no arguments prints everything
  7. Hanging up is the hard part
  8. A subagent's model is not the main model
  9. grammY's bot.start() never returns
  10. A guard hook that errors lets the command run
  11. Making the Claude GitHub Action open the PR
  12. There is no author_association on sender

Databases 7

  1. Two places MySQL wants another layer
  2. CONVERT TO runs after MODIFY
  3. Postgres in Docker ignored my POSTGRES_ settings
  4. A missing index, found with rack-mini-profiler
  5. Connecting Rails to Amazon RDS with SSL
  6. Remote Database Connection via ECS Fargate
  7. Execute pg_dump against a remote database

Cloudflare 5

  1. Wrangler used a token I forgot about
  2. One API route on a static Astro site
  3. WARP blamed 1Password, but Docker was in the way
  4. Claude Code kept dropping on my home Wi-Fi
  5. 633 Worker versions and no way to delete them

This site 4

  1. Make Me A Workflow
  2. Transitioning to a Notebook-style format
  3. HTTPS For Your Static S3 Website
  4. Static Website Hosting on Amazon S3

Python and data 3

  1. setuptools 82 removed pkg_resources
  2. Becoming Friends with Pandas
  3. Jupyter Notebooks: My New Favorite REPL

Integrations 3

  1. 30909 doesn't say what's missing
  2. Configure Bugsnag to sync with Trello
  3. Slack bot for API Documentation

Rust 1

  1. Upgrading a Rust Project

2026

  1. A time on a date column moved the month
  2. ECS stopped tasks that were still booting
  3. Two places MySQL wants another layer
  4. CONVERT TO runs after MODIFY
  5. chruby picked the broken Ruby
  6. Three ways Puma didn't restart
  7. compose run used the new image, the server didn't
  8. Notes from Rails 6.1 to 8.1 in a day
  9. Where the email link 404s came from
  10. Wrangler used a token I forgot about
  11. A new cookie domain needs a new cookie name
  12. A hash without braces became keyword arguments
  13. The caller talked during a tool call
  14. A frontend deploy broke LLM streaming
  15. My cd hook was filling Claude's context
  16. ECS Exec is not a shell
  17. localhost was IPv6 on one end
  18. Logs Insights months start in UTC
  19. 30909 doesn't say what's missing
  20. driven_by replaced my Chrome driver
  21. The relation ran after the block ended
  22. One API route on a static Astro site
  23. WARP blamed 1Password, but Docker was in the way
  24. Two Ruby managers, one gem directory
  25. Claude Code kept dropping on my home Wi-Fi
  26. Setting config.hosts dropped the defaults
  27. Time.local doesn't know the Rails zone
  28. Claude Desktop started my MCP server with Node 10
  29. export with no arguments prints everything
  30. Hanging up is the hard part
  31. Capybara waited for a request my test never checked
  32. A subagent's model is not the main model
  33. Tailwind didn't scan my UI package
  34. find -path "./node_modules/*" only matches the top level
  35. setuptools 82 removed pkg_resources
  36. An old PostCSS config broke the Astro build
  37. grammY's bot.start() never returns
  38. A guard hook that errors lets the command run
  39. 633 Worker versions and no way to delete them
  40. Making the Claude GitHub Action open the PR
  41. There is no author_association on sender