January 21, 2025
Paweł Dąbrowski
Founder / Software Engineer
Recently, Basecamp, the creators of Ruby on Rails, released a solution for deploying it on bare metal servers. Kamal is Capistrano for Containers and is an alternative solution to Capistrano, Kubernetes, and Docker Swarm.
If you want to deploy your Rails application on a bare metal server but do not like how Kamal works, there is a good and more straightforward alternative: Dokku. It’s Heroku, but for your own server.
I’ve been using Dokku for years, and it works perfectly. With support for Sidekiq, ElasticSearch, and other application components, you can prepare the production environment for Rails in minutes. In this article, I will walk you through preparing the server environment and deploying the Rails application. As a bonus, I will show you a tool called Deployless that automates the whole setup for your production and staging environments.
The first step is to download and run the Dokku installation script on the server. The solution can be installed on the newest Ubuntu and Debian 11+ distributions.
SSH to your server, download the installation script, and run it:
wget -NP . https://dokku.com/install/v0.35.12/bootstrap.sh
sudo DOKKU_TAG=v0.35.12 bash bootstrap.sh
The installation should take around 5 minutes to complete. Once it’s finished, the next step is to add an SSH key so you can deploy your application using GIT just like it’s possible with Heroku:
echo "public ssh key" | sudo dokku ssh-keys:add admin
In the same way, you can add the next users who can deploy the application. To add access to the server, edit ~/.ssh/authorized_keys
and paste the SSH key of the person you want to authenticate.
Creating a new application is straightforward. All you need to do is to invoke the create command:
dokku apps:create myrailsapp
If you push the application right now, Dokku will detect that you are pushing a Rails application and install the specified Ruby version. However, we can’t do it now because we haven’t determined the correct environment variables yet.
Let’s start with creating a sample Rails application with support for PostgreSQL as a database engine:
rails _8.0.1_ new myrailsapp --skip-kamal —skip-docker --skip-solid --skip-thurster --skip-devcontainer --database=postgresql
We can now return to the server and take care of the database.
Master the art of database optimization in Rails applications. Learn proven techniques to improve your Ruby on Rails application performance.
We have already done a base configuration of Dokku. Regarding additional components for our application, Dokku uses plugins to handle them easily. The first step is to install the PostgreSQL plugin:
sudo dokku plugin:install https://github.com/dokku/dokku-postgres.git postgres
The next step is to create the database itself. Because we operate on the containers, we must make the database accessible for our app containers. This process is called linking.
Let’s create the database:
dokku postgres:create myrailsapp-db
Then, let’s link the database container to the application:
dokku postgres:link myrailsapp-db myrailsapp
Thanks to this operation, Dokku automatically configured the DATABASE_URL
environment variable that contains the connection string for our application. Regarding environment variables, let’s add the base variables for the application right now.
Basic Rails application, needs some base environment variables to work correctly. For example, the SECRET_KEY_BASE
value is used to generate various security keys for the environment. You can generate the desired value by running rails secret and then copying the value.
Besides the mentioned environment variable, let’s also add some others to make sure we are running the app in production mode:
dokku config:set myrailsapp SECRET_KEY_BASE=mysecret RAILS_ENV=production RACK_ENV=production RAILS_LOG_TO_STDOUT=enabled RAILS_SERVE_STATIC_FILES=enabled
Once you add the environment variables, Dokku will handle application update, so you don’t have to worry about it. Since we haven’t pushed the application's source code, the process takes just a second.
The easiest way to configure the domain with the Dokku application is to define the A record in DNS settings pointing to the server IP address. I’m going to do this right now, using the myrailsapp.impactahead.com subdomain for the purpose of this article.
I’m using Cloudflare, but the process will be the same for most DNS providers. Copy the server IP address, select A record, define the subdomain (or leave the subdomain field empty if you want to use the domain), and paste the IP record. If you are also using Cloudflare, turn off the proxy status.
Go back to the server; the next step is to clear default domain values and add the domain:
dokku domains:clear-global
dokku domains:clear myrailsapp
dokku domains:add myrailsapp myrailsapp.impactahead.com
The process of configuring the SSL certificates is also simple. Dokku uses the Lets Encrypt plugin to handle all related processes. Let’s install it:
sudo dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git letsencrypt
The last step of the configuration is the information about e-mail to which information about SSL expiration will be delivered:
dokku letsencrypt:set myrailsapp email your-email
We can now enable SSL for our application. The plugin will attempt to configure the certificate and update the server configuration. At this stage, the DNS change should already be propagated; otherwise, the SSL verification will fail. Execute the following command:
dokku letsencrypt:enable myrailsapp
After the SSL is generated, we are done with the server configuration. If you don’t want to renew the SSL certificate manually, you can set up the cron to do it automatically:
dokku letsencrypt:cron-job --add
We need to tell Dokku that we want to use a worker to run our web server. To do this, create Procfile
file in the main directory with the following lines:
web: bundle exec rails server -p $PORT -e $RAILS_ENV
release: bundle exec rails db:migrate
Update Gemfile
with your Ruby version declaration and run bundle install:
ruby ‘3.4.1’
The last step is to add GIT remote so we can deploy our application in a Heroku way:
git remote add dokku dokku@ip-address:myrailsapp
We can now push our application:
git add .
git commit -m "Hello world"
git push dokku main
Dokku doesn’t need Dockerfile to correctly install dependencies for the Rails app; specified Ruby version is enough. After the deployment is complete, you can visit the configured domain, but you will see a 404 error. We haven’t specified the root page. You can create a simple HomeController
with an index action that will render "Hello world" and make it a root page. Deploy the app again, and you should see the welcome screen.
Master the art of database optimization in Rails applications. Learn proven techniques to improve your Ruby on Rails application performance.
Usually, deploying the Rails application on Dokku requires the same steps. To make the deployment configuration much faster, I created the Deployless gem, which makes it effortless. While your Dokku configuration is prepared, you can make yourself a coffee.
Start with adding the gem to the development group:
group :development do
gem "deployless"
end
Run bundle install
to install the library. Prepare the IP address of your server and the domain you would like to use for the application. Run the interactive setup:
dpls init
You need to provide some basic details, such as the server IP address, username, path to your SSH key, and application name. Once you are finished, a .deployless.yml
file is created, and the .gitignore
file is updated to exclude this configuration file from the repository.
You can now trigger the deployment configuration for the production environment by invoking the following command:
dpls production prepare
The library will connect to the server, install Dokku, and prepare all components for your application. All you have to do is to ensure that a proper DNS record is added to make the domain available for the server - the library will stop the process and ask if this step is ready to finish the domain and SSL configuration.
Once the process is finished, you can add a new GIT remote and push the code to deploy the application. If you inspect .deployless.yml
file, you will also notice the environment variables. Once you update them, you can force the change on the server by running:
dpls production update-env
Connecting to the production Rails console is also effortless as executing the following command:
dpls production console