Deploy Rails on Your Own Server
Deploying a Rails application on your own server is one of the most valuable exercises a web developer can do, even if you eventually choose a PaaS for production. The process teaches you how the pieces fit together: Linux basics, Ruby version management, process supervision, reverse proxying, database operations and the monitoring that catches problems before your users do. This path takes you from a blank VPS to serving production traffic, step by step, based on the real problems that tend to surface first. For the broader context on deployment decisions and trade-offs, see the Rails Deployment topic page.
Who this is for
- Rails developers who have only deployed to Heroku or similar platforms and want to understand what happens underneath
- Developers taking on their first solo deployment for a side project or small company
- Anyone preparing for a role that includes infrastructure responsibilities
Prerequisites
- Comfortable with the command line (navigating directories, editing files, running commands)
- A working Rails application that runs in development
- Basic understanding of HTTP requests and responses
- Access to a VPS provider account (DigitalOcean, Hetzner, Linode, Vultr or similar)
- An SSH key pair
Step 1: Server provisioning and initial security
What you will do: Spin up a fresh VPS, configure SSH access, set up a firewall and create a non-root deploy user.
Why it matters: Every production deployment starts with a machine. Getting the initial security right—key-only SSH, firewall rules, a dedicated deploy user—prevents the most common server compromise vectors.
Key actions:
- Provision a VPS with Ubuntu LTS (22.04 or 24.04)
- Disable root SSH login and password authentication
- Set up UFW firewall allowing only SSH (22), HTTP (80) and HTTPS (443)
- Create a deploy user with sudo access
- Configure SSH key authentication for the deploy user
Common blocker: Locking yourself out by misconfiguring the firewall or SSH before confirming your key works. Always test SSH access in a separate terminal before closing your current session.
Expected time: 30-45 minutes
Step 2: Ruby version management and application setup
What you will do: Install a Ruby version manager, compile the correct Ruby version, install Bundler and get your application code onto the server.
Why it matters: Ruby version mismatches between development and production are one of the most common sources of deployment failures. Getting this right means your Gemfile.lock resolves the same way on the server as on your laptop.
Key actions:
- Install rbenv and ruby-build (or asdf with the Ruby plugin)
- Compile the Ruby version specified in your
.ruby-versionfile - Install Bundler at the version locked in your Gemfile.lock
- Clone your application repository to the server
- Run
bundle install --deployment --without development test - Verify
bin/rails consolestarts in production mode
Common blocker: Missing system libraries needed to compile native gem extensions. The error messages point at the C compilation failure but not at the missing package. For Ubuntu, install build-essential, libpq-dev, libssl-dev, libyaml-dev and zlib1g-dev before running bundle install.
Expected time: 30-45 minutes
Step 3: PostgreSQL setup and database creation
What you will do: Install PostgreSQL, create a production database and database user, configure database.yml and run migrations.
Why it matters: The database is the persistent state of your application. Getting the setup right includes not just creating the database, but configuring authentication, setting connection limits and establishing a backup routine.
Key actions:
- Install PostgreSQL from the official repository
- Create a dedicated PostgreSQL user for your application
- Create the production database with the application user as owner
- Configure
config/database.ymlfor the production environment - Set the
DATABASE_URLenvironment variable as an alternative - Run
bin/rails db:migrate RAILS_ENV=production - Verify the database schema matches expectations
Common blocker: PostgreSQL authentication method confusion. The default pg_hba.conf on Ubuntu uses peer authentication for local connections, which means the OS user must match the PostgreSQL user. For application connections, switch to md5 or scram-sha-256 authentication.
Expected time: 20-30 minutes
Step 4: Puma configuration and process management
What you will do: Configure Puma for production, create a systemd service unit and verify the application starts and restarts reliably.
Why it matters: Puma is the process that actually runs your Rails code. Configuring it correctly—worker count, thread count, socket binding, preloading—determines your application's capacity and resource usage. Process management through systemd ensures it starts on boot and restarts on failure.
Key actions:
- Configure
config/puma.rbfor production (workers, threads, bind socket) - Set
preload_app!for memory efficiency - Add
on_worker_bootcallback for database connection re-establishment - Create a systemd service unit at
/etc/systemd/system/puma.service - Configure environment variables via an EnvironmentFile
- Enable the service and start it
- Verify the application responds on the socket
Common blocker: Puma starts but requests hang. This is usually a database connection pool mismatch: more Puma threads than pool setting in database.yml. Set pool to at least your thread count.
Expected time: 30 minutes
Step 5: Nginx reverse proxy and SSL
What you will do: Install Nginx, configure it as a reverse proxy for Puma, set up SSL with Let's Encrypt and configure static asset serving.
Why it matters: Nginx handles the parts of HTTP that Puma should not: SSL termination, request buffering, static file serving and connection management. It also provides the security headers and caching rules that production sites need. The detailed Nginx configuration walkthrough is in the Nginx for Rails Apps guide.
Key actions:
- Install Nginx
- Create an Nginx server block for your domain
- Configure the upstream to point at Puma's socket
- Set up proxy headers (
X-Forwarded-For,X-Forwarded-Proto,Host) - Configure static asset serving from
public/ - Install Certbot and obtain an SSL certificate
- Configure HTTP to HTTPS redirect
- Add security headers (
X-Content-Type-Options,X-Frame-Options) - Test the full request path from browser to application
Common blocker: 502 Bad Gateway. This means Nginx cannot reach Puma. Check that the socket path in the Nginx upstream matches the socket path in Puma's bind configuration, and that Nginx has permission to read the socket file.
Expected time: 45 minutes
Step 6: Background jobs, monitoring and ongoing operations
What you will do: Set up Sidekiq for background job processing, configure basic monitoring and establish deployment and maintenance routines.
Why it matters: A running application is not a deployed application. You also need background jobs, health monitoring and a deployment process that does not require SSH-ing into the server and running commands by hand.
Key actions:
- Install and start Redis for Sidekiq
- Configure Sidekiq with a systemd service unit
- Set up job queues and priorities in
config/sidekiq.yml - Configure a basic health check endpoint
- Set up an external uptime monitor
- Write a deployment script or Capistrano configuration
- Document your server setup for future reference
- Set up log rotation for application and service logs
- Schedule automated database backups
Common blocker: Sidekiq starts but jobs are not processed. Check that Redis is running and reachable, that the Sidekiq service is using the same Redis URL as your application, and that your job classes are loadable in the production environment.
Expected time: 45-60 minutes
Expected outcomes
After completing this path, you will be able to:
- Set up a production-ready server from a blank VPS
- Deploy a Rails application with proper process management
- Configure Nginx as a reverse proxy with SSL
- Manage PostgreSQL in production, including backups
- Run background jobs reliably with Sidekiq
- Monitor your application and respond to common failures
- Articulate the deployment architecture to team members
What to read next
- Rails Deployment — the full topic reference
- Nginx for Rails Apps — detailed Nginx configuration
- PostgreSQL Indexing for Rails — database performance
- Debugging Production Rails Issues — when things go wrong