Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save fzrhrs/c96561a7db7123a07e08e1c9537b7cec to your computer and use it in GitHub Desktop.
Save fzrhrs/c96561a7db7123a07e08e1c9537b7cec to your computer and use it in GitHub Desktop.
Part 3 of 3: Rails Deployment Ubuntu 2404 (2024)

Now, the final part of the initial deployment for Rails with RVM, Postgres, Ubuntu, NGINX, Capistrano, Puma 6 & Systemd.

Go to /etc/nginx/sites-available

Create a conf file, I usually name it after my app. For this tutorial, let's call it "website":

upstream puma_website {
  server unix:///home/deploy/website/shared/sockets/puma.sock;
}

server {
  listen 80;
  server_name website.com;

  root /home/deploy/website/current/public;
  access_log /home/deploy/website/current/log/nginx.access.log;
  error_log /home/deploy/website/current/log/nginx.error.log info;

  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
  }

  try_files $uri/index.html $uri @puma_website;
  location @puma_website {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;

    proxy_pass http://puma_website;
  }

  location /cable {
    proxy_pass http://puma_website/cable;
    proxy_http_version 1.1;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
  }

  error_page 500 502 503 504 /500.html;
  client_max_body_size 10M;
  keepalive_timeout 10;
}

Copy this sample and change the application name accordingly. The fastest to do it is by opening the file using vi, and then use this. While in ESC mode, type:

:%s/oldtext/newtext/gc

Adding 'c' at the end of the command will confirm each replacement before it is applied.

Check if all is good:

sudo nginx -t

If all is good, then do a symlink to /etc/nginx/sites-enabled. I usually go to the folder first, and create a symlink from there.

Assuming you are in the directory, do:

sudo ln -s ../sites-available/website .

Then, restart nginx. Out of habit, I do this one bye one:

sudo service nginx stop
sudo service nginx start
sudo service nginx status

But you can also do this in one command:

sudo service nginx restart

If you haven't run this command, please do so before continuing:

bundle exec cap production puma:install

Read more about this here: https://github.com/seuros/capistrano-puma

1. Create puma.rb

Ensure in your application folder, specifically app_name/shared, you create a file called puma.rb. In it, it should have this content:

#!/usr/bin/env puma

directory '/home/deploy/website/current'
rackup "/home/deploy/website/current/config.ru"
environment 'production'

tag ''

pidfile "/home/deploy/website/shared/tmp/pids/puma.pid"
state_path "/home/deploy/website/shared/tmp/pids/puma.state"
stdout_redirect '/home/deploy/website/shared/log/puma_access.log', '/home/deploy/website/shared/log/puma_error.log', true

threads 0, 16

bind 'unix:///home/deploy/website/shared/sockets/puma.sock'

workers 0

restart_command 'bundle exec puma'

prune_bundler

on_restart do
  puts 'Refreshing Gemfile'
  ENV["BUNDLE_GEMFILE"] = ""
end

2. Create service file.

Assuming your EC2 instance hosts multiple applications, you'll want to create separate systemd service files for each Puma instance. This way, each app can be managed independently, and changes to one service won't affect the others.

This will also work even if you only have one application in the instance.

For each application, you should create a unique service file. For example, if your current app is website, create a service file named website_puma_production.service:

sudo vi /etc/systemd/system/website_puma_production.service

Here's what the service file look like for website:

[Unit]
Description=Puma HTTP Server for website
After=network.target

[Service]
Type=simple
User=deploy
WorkingDirectory=/home/deploy/website/current
ExecStart=/bin/bash -lc 'bundle exec puma -C /home/deploy/website/shared/puma.rb'
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
  • Naming Convention: Name the service file according to the app it serves (e.g., appname_puma_stage.service).
  • WorkingDirectory: Ensure this points to the correct directory for each application.
  • ExecStart: Adjust this as needed for each application’s configuration.

Reload systemd:

sudo systemctl daemon-reload

Start and enable each service. For example, for website:

sudo systemctl enable website_puma_production
sudo systemctl start website_puma_production

And finally, just to be safe, restart nginx:

sudo systemctl restart nginx

3. Change deploy folder permission

Capistrano uses SSH to log in and perform operations such as creating directories (releases, shared), cloning the repository, and symlinking. If the folder doesn’t have executable permissions (x) for the user running Capistrano (typically the deploy user), it will result in a "Permission denied" error when trying to access or modify the contents.

sudo chmod 755 deploy

Rails Deployment Ubuntu Series:

  1. Part 1 of 3: Rails Deployment Ubuntu 24.04 (2024)
  2. Part 2 of 3: Rails Deployment Ubuntu 24.04 (2024)
  3. Part 3 of 3: Rails Deployment Ubuntu 2404 (2024)
  4. Use passwordless sudo
@imidsac
Copy link

imidsac commented Nov 3, 2024

Hi
I have a mistake, I can't correct it.

`
* Enabling systemd notification integration
Puma starting in single mode...
* Puma version: 6.4.3 (ruby 3.2.5-p208) ("The Eagle of Durango")
*  Min threads: 5
*  Max threads: 5
*  Environment: production
*          PID: 634947
! Unable to load application: ArgumentError: Missing `secret_key_base` for 'production' environment, set this string with `bin/rails credentials:edit`
bundler: failed to load command: puma (/var/www/Tringa/shared/bundle/ruby/3.2.0/bin/puma)
/var/www/Tringa/shared/bundle/ruby/3.2.0/gems/railties-7.1.4.1/lib/rails/application.rb:661:in `validate_secret_key_base': Missing `secret_key_base` for 'production' environment, set this string with `bin/rails credentials:edit` (ArgumentError)

        raise ArgumentError, "Missing `secret_key_base` for '#{Rails.env}' environment, set this string with `bin/rails credentials:edit`"
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
	from /var/www/Tringa/shared/bundle/ruby/3.2.0/gems/railties-7.1.4.1/lib/rails/application.rb:483:in `secret_key_base'
	from /var/www/Tringa/shared/bundle/ruby/3.2.0/gems/devise-4.9.4/lib/devise/secret_key_finder.rb:24:in `key_exists?'
	from /var/www/Tringa/shared/bundle/ruby/3.2.0/gems/devise-4.9.4/lib/devise/secret_key_finder.rb:16:in `find'
	from /var/www/Tringa/shared/bundle/ruby/3.2.0/gems/devise-4.9.4/lib/devise/rails.rb:41:in `block in <class:Engine>'
	from /var/www/Tringa/shared/bundle/ruby/3.2.0/gems/railties-7.1.4.1/lib/rails/initializable.rb:32:in `instance_exec'
	from /var/www/Tringa/shared/bundle/ruby/3.2.0/gems/railties-7.1.4.1/lib/rails/initializable.rb:32:in `run'
	from /var/www/Tringa/shared/bundle/ruby/3.2.0/gems/railties-7.1.4.1/lib/rails/initializable.rb:61:in `block in run_initializers'
	from /home/imidsac/.rvm/ru

nginx.error.log

`
[[](2024/11/03](url) 14:10:38 [alert] 703#703: *765 768 worker_connections are not enough while connecting to upstream, client: 139.162.210.54, server: aap.malinuse.com, request: "GET / HTTP/1.0", upstream: "http://139.162.210.54:80/", host: "aap.malinuse.com"
2024/11/03 14:10:39 [info] 703#703: *1 client 35.93.95.86 closed keepalive connection
2024/11/03 14:10:40 [alert] 703#703: *1531 768 worker_connections are not enough while connecting to upstream, client: 139.162.210.54, server: aap.malinuse.com, request: "GET / HTTP/1.0", upstream: "http://139.162.210.54:80/", host: "aap.malinuse.com"
2024/11/03 14:10:41 [info] 703#703: *767 client 35.93.95.86 closed keepalive connection
2024/11/03 14:19:51 [error] 25558#25558: *8 connect() failed (111: Connection refused) while connecting to upstream, client: 139.162.210.54, server: aap.malinuse.com, request: "GET / HTTP/1.1", upstream: "http://139.162.210.54:3000/", host: "aap.malinuse.com"
2024/11/03 14:19:51 [info] 25558#25558: *8 client 139.162.210.54 closed keepalive connection
2024/11/03 14:21:18 [error] 60959#60959: *1 connect() failed (111: Connection refused) while connecting to upstream, client: 139.162.210.54, server: aap.malinuse.com, request: "GET / HTTP/1.1", upstream: "http://139.162.210.54:3000/", host: "aap.malinuse.com"
2024/11/03 14:21:18 [info] 60959#60959: *1 client 139.162.210.54 closed keepalive connection
2024/11/03 14:44:18 [error] 60959#60959: *12 connect() failed (111: Connection refused) while connecting to upstream, client: 139.162.210.54, server: aap.malinuse.com, request: "GET / HTTP/1.1", upstream: "http://139.162.210.54:3000/", host: "aap.malinuse.com"
2024/11/03 14:44:18 [info] 60959#60959: *12 client 139.162.210.54 closed keepalive connection)

@fzrhrs
Copy link
Author

fzrhrs commented Nov 10, 2024

@imidsac

Hi, I think it's because you are missing the "secret_key_base" in your credentials.

You can check by running EDITOR=vi bin/rails credentials:edit in your local to see if you have setup the secret_key_base. If you did, and it's showing the error in production, it might be because your have yet to include the master_key in the production server.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment