Debugging Rails App With Docker Compose
How to use Byebug in a dockerized rails app
Context
Have you ever faced an issue where you want to debug a live session, went ahead and attach a byebug in the code, the app just gave up and paused itself until the end of time unless you restart the server?
Step 1: Setup your environment
- Install Docker on your system.
- Feel free to use my minimal app if you don’t have one already once Docker is installed.
- You can go straight to Step 4 if you are using the minimal app.
Step 2: Modify docker compose file
- Add this to your
docker-compose.yml
under networks in web service:
web:
...
stdin_open: true
tty: true
What this does is allowing us to start an interactive shell in a container using docker compose so we are able to enter byebug commands such as continue
, step
and frame
.
Step 3: Create Database Instances
You’ll need to create databases inside of your container to be able to visit any API:
- Spin up database and rails app in the container in detached mode so we can progress in the same terminal session:
docker-compose up -d
- Go to the app running inside our container:
docker-compose exec web bash
- Create databases for our app:
rails db:create
- You can exit from the bash session once the database is created by simply typing
exit
Step 4: Attach your terminal to the container
Attach your terminal to the container: docker attach [container_id]
You may find your container_id by using: docker container ls
At this point you should be able to see every input, output and errors from our running container.
Let’s try to attach a byebug in your codebase for instance:
Then you can try visiting the API (or http://127.0.0.1/health/check
in my example)
Your app should pause when the program execution reach the byebug line and now you can enter byebug commands in the attached docker process:
You can always detach from this Docker process by entering the escape sequence: Ctrl+P
then Ctrl+Q
.
Summary
This is a slick solution for someone who prefer to debug a live session even though I always debug my app via test cases with RSpec so I don’t have to worry about overheads such as request timeout and mocking failure response from a certain module that the API is depending on all that kind of stuff.