Debugging a Rails-Docker-PostgreSQL Integration: A Developer's Journey

Debugging a Rails-Docker-PostgreSQL Integration: A Developer's Journey

The Initial Challenge

I encountered a failing Rails API service that revealed multiple interconnected issues in my full-stack ERP application. What started as a simple "Rails API not running" message turned into a deep dive through logging configurations, Docker networking, PostgreSQL authentication, and environment variable inheritance.

The Missing Logs Directory

My first discovery came when attempting to start the Rails server with output redirection:

nohup bin/rails server -p 3002 > ../logs/rails.log 2>&1 &

The command failed with: ../logs/rails.log: No such file or directory

I revealed the issue by examining my project structure:

ls -la . && ls -la .. && find . -type d -name "logs" 2>/dev/null | head -10

This command showed me that no logs directory existed at the project root. The startup script expected to redirect Rails output to ../logs/rails.log, but the directory simply wasn't there. Creating it was straightforward:

mkdir -p logs && ls -la logs/

Database Connection Failures

With logging fixed, Rails started but immediately encountered database connection errors. The HTTP 500 responses revealed authentication failures. I investigated by checking the database migration status:

cd backend && bin/rails db:migrate:status

This revealed the core issue: FATAL: password authentication failed for user "postgres". Rails couldn't connect to PostgreSQL. I needed to understand what credentials Rails was actually using.

Uncovering Configuration Mismatches

I examined the Rails database configuration to understand what it expected:

cat config/database.yml | grep -A 10 "development:"
head -n 30 config/database.yml

The configuration showed Rails was using environment variables with ERB templating:

  • DATABASE_USER defaulting to "postgres"
  • DATABASE_PASSWORD defaulting to "postgres"
  • DATABASE_HOST defaulting to "localhost"
  • DATABASE_PORT defaulting to 5432

I then checked my .env file for existing configurations:

cat .env | grep -E "DATABASE_|DB_" | grep -v "^#"

This revealed conflicting configurations. My .env contained DB_PASSWORD (not DATABASE_PASSWORD) and a DATABASE_URL pointing to a Docker service name "db" that wouldn't resolve from my host machine.

Docker Container Investigation

I discovered my PostgreSQL was running in Docker, mapped to port 5433 (not the default 5432):

docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Ports}}" | grep -E "postgres|5432|5433"

The output showed: pcvn-postgres postgres:15-alpine 0.0.0.0:5433->5432/tcp

I inspected the container to understand its authentication configuration:

docker inspect pcvn-postgres | grep -B 2 -A 2 "POSTGRES"

This revealed the critical mismatch: PostgreSQL was configured with POSTGRES_USER=pcvn, not "postgres". I had been trying to authenticate with the wrong username all along.

Environment Variable Solutions

I attempted several approaches to set the environment variables correctly. First, I tried exporting them:

export DATABASE_HOST=localhost DATABASE_PORT=5433 DATABASE_USER=pcvn DATABASE_PASSWORD=pcvn_prod_db_2025_secure_password_8f3k9m2p

I verified Rails could see the configuration:

bin/rails runner "puts ActiveRecord::Base.connection_db_config.configuration_hash.to_json"

To test the actual database connection, I created a simple test script:

echo "puts 'Testing database connection...'" > test_db.rb
echo "puts ActiveRecord::Base.connection.execute('SELECT 1').values" >> test_db.rb
bin/rails runner test_db.rb

Process Management Challenges

I discovered multiple Rails processes running with incorrect configurations. I found them using:

ps aux | grep -E "rails|puma" | grep -v grep

I had to clean up these processes before starting fresh:

pkill -f "rails server"
kill [specific_pid]
rm -f tmp/pids/server.pid

The Final Working Solution

After clearing all obstacles, I started Rails with the correct configuration passed directly:

DATABASE_HOST=localhost DATABASE_PORT=5433 DATABASE_USER=pcvn DATABASE_PASSWORD=pcvn_prod_db_2025_secure_password_8f3k9m2p bin/rails server -p 3002 -d

The -d flag made Rails daemonize itself properly, preserving the environment variables.

Route Discovery and Validation

Initially, I tested /health and received a 404 error. This actually confirmed Rails was working - it just didn't have that specific route. I discovered the correct endpoint:

bin/rails routes | grep -i health

Rails 8 uses /up as its health check endpoint, not /health. Testing the correct endpoint:

curl -i http://localhost:3002/up

The response was a beautiful HTTP 200 OK with a green background HTML page - Rails' way of saying everything was healthy.

Key Lessons Learned

Through this debugging journey, I learned that containerized database authentication requires perfect alignment of three elements: network path (host and port), username, and password. Having two out of three correct still results in failure.

The systematic debugging approach I employed involved checking actual running configurations versus expected configurations, verifying each layer of the stack independently, and understanding that error messages are clues, not roadblocks. The 500 errors pointed to database issues, authentication failures revealed credential mismatches, and the 404 error actually confirmed my fixes had worked.

Environment variable inheritance in shell sessions proved more complex than expected. Variables set with export remain in the current shell but don't automatically pass through tools like nohup. Using Rails' built-in daemon mode (-d) or passing variables directly in the command line proved more reliable.

The distinction between Docker service names and actual hostnames became clear. While Docker Compose services can reach each other by service name, applications running on the host must use localhost with the mapped ports.

The Working Architecture

My final working setup consisted of PostgreSQL running in Docker container "pcvn-postgres" on port 5433, Rails running on the host machine on port 3002, and proper environment variables bridging the configuration gap between Docker and Rails. This hybrid approach - containerized services with host-based application development - provided both consistency and development flexibility.

This debugging experience reinforced that modern web development requires understanding multiple layers of abstraction. From shell environments through application frameworks to container networking, each layer must be properly configured and aligned for the complete system to function.


If you enjoyed this article, you can also find it published on LinkedIn and Medium.