Ruby on Rails ist ein produktives Webframework nach dem Convention-over-Configuration-Prinzip. Für den Produktionsbetrieb wird Puma als Application Server zusammen mit Nginx empfohlen.

Architektur

Client → Nginx (Port 80/443)
              ↓
         Puma (Unix Socket)
              ↓
         Rails App
              ↓
         PostgreSQL

Ruby installieren

rbenv (empfohlen)

# Dependencies
apt install git curl autoconf bison build-essential \
    libssl-dev libyaml-dev libreadline-dev zlib1g-dev \
    libncurses5-dev libffi-dev libgdbm-dev

# rbenv installieren
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
source ~/.bashrc

# ruby-build
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build

# Ruby installieren
rbenv install 3.3.0
rbenv global 3.3.0

# Prüfen
ruby -v

System-Ruby (Alternative)

apt install ruby ruby-dev ruby-bundler

Rails-Projekt

Neues Projekt

gem install rails
rails new myapp --database=postgresql
cd myapp

Bestehendes Projekt

mkdir -p /var/www/myapp
cd /var/www/myapp
git clone https://github.com/user/myapp.git .

Dependencies installieren

bundle install --deployment --without development test

Datenbank

PostgreSQL einrichten

sudo -u postgres psql

CREATE DATABASE myapp_production;
CREATE USER myapp WITH PASSWORD 'sicheres_passwort';
GRANT ALL PRIVILEGES ON DATABASE myapp_production TO myapp;
\q

database.yml

# config/database.yml

production:
  adapter: postgresql
  encoding: unicode
  database: <%= ENV['DATABASE_NAME'] %>
  username: <%= ENV['DATABASE_USER'] %>
  password: <%= ENV['DATABASE_PASSWORD'] %>
  host: <%= ENV['DATABASE_HOST'] || 'localhost' %>
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

Migrationen

RAILS_ENV=production rails db:migrate

Puma-Konfiguration

config/puma.rb

# config/puma.rb

# Umgebung
rails_env = ENV.fetch("RAILS_ENV") { "production" }
environment rails_env

# Threads
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
threads threads_count, threads_count

# Workers (Multi-Process)
workers ENV.fetch("WEB_CONCURRENCY") { 2 }

# Socket
if rails_env == "production"
  bind "unix:///run/puma/myapp.sock"
else
  port ENV.fetch("PORT") { 3000 }
end

# Preload für Copy-on-Write
preload_app!

# Worker-Boot
on_worker_boot do
  ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
end

# PID und State
pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }
state_path "tmp/pids/puma.state"

# Logging
if rails_env == "production"
  stdout_redirect "/var/log/puma/myapp.stdout.log",
                  "/var/log/puma/myapp.stderr.log",
                  true
end

# Graceful Restart
plugin :tmp_restart

Systemd-Service

puma.service

# /etc/systemd/system/puma-myapp.service

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

[Service]
Type=notify
WatchdogSec=10

User=www-data
Group=www-data
WorkingDirectory=/var/www/myapp
RuntimeDirectory=puma

Environment=RAILS_ENV=production
Environment=RAILS_LOG_TO_STDOUT=true
EnvironmentFile=/var/www/myapp/.env

ExecStart=/home/deploy/.rbenv/shims/bundle exec puma -C config/puma.rb
ExecReload=/bin/kill -SIGUSR1 $MAINPID

Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Service starten

mkdir -p /var/log/puma
chown www-data:www-data /var/log/puma

systemctl daemon-reload
systemctl enable puma-myapp
systemctl start puma-myapp

Nginx-Konfiguration

# /etc/nginx/sites-available/myapp

upstream myapp {
    server unix:/run/puma/myapp.sock fail_timeout=0;
}

server {
    listen 80;
    server_name example.de www.example.de;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.de www.example.de;

    ssl_certificate /etc/letsencrypt/live/example.de/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.de/privkey.pem;

    root /var/www/myapp/public;

    client_max_body_size 10M;

    access_log /var/log/nginx/myapp-access.log;
    error_log /var/log/nginx/myapp-error.log;

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

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

    try_files $uri/index.html $uri @myapp;

    location @myapp {
        proxy_pass http://myapp;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_redirect off;
    }

    error_page 500 502 503 504 /500.html;
}
ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

Umgebungsvariablen

.env-Datei

# /var/www/myapp/.env

RAILS_ENV=production
SECRET_KEY_BASE=dein_sehr_langes_secret_hier
DATABASE_NAME=myapp_production
DATABASE_USER=myapp
DATABASE_PASSWORD=sicheres_passwort
DATABASE_HOST=localhost
RAILS_MAX_THREADS=5
WEB_CONCURRENCY=2
RAILS_SERVE_STATIC_FILES=true

Secret Key generieren

RAILS_ENV=production rails secret

Asset-Pipeline

Assets kompilieren

RAILS_ENV=production rails assets:precompile

Mit Webpack

RAILS_ENV=production rails assets:precompile
# Oder mit Webpacker
RAILS_ENV=production rails webpacker:compile

Sidekiq (Background Jobs)

Installation

# Gemfile
gem 'sidekiq'
bundle install

Konfiguration

# config/sidekiq.yml

:concurrency: 5
:queues:
  - default
  - mailers
  - critical
# config/initializers/sidekiq.rb

Sidekiq.configure_server do |config|
  config.redis = { url: ENV.fetch('REDIS_URL') { 'redis://localhost:6379/0' } }
end

Sidekiq.configure_client do |config|
  config.redis = { url: ENV.fetch('REDIS_URL') { 'redis://localhost:6379/0' } }
end

Sidekiq-Service

# /etc/systemd/system/sidekiq-myapp.service

[Unit]
Description=Sidekiq for myapp
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/myapp
EnvironmentFile=/var/www/myapp/.env

ExecStart=/home/deploy/.rbenv/shims/bundle exec sidekiq -C config/sidekiq.yml

Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Capistrano Deployment

Installation

# Gemfile
group :development do
  gem 'capistrano', '~> 3.17'
  gem 'capistrano-rails', '~> 1.6'
  gem 'capistrano-rbenv', '~> 2.2'
  gem 'capistrano3-puma', '~> 5.2'
end
bundle install
cap install

Capfile

# Capfile

require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rbenv'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano/puma'

install_plugin Capistrano::Puma
install_plugin Capistrano::Puma::Systemd

deploy.rb

# config/deploy.rb

set :application, 'myapp'
set :repo_url, 'git@github.com:user/myapp.git'

set :deploy_to, '/var/www/myapp'
set :branch, 'main'

set :rbenv_type, :user
set :rbenv_ruby, '3.3.0'

set :linked_files, %w[.env config/master.key]
set :linked_dirs, %w[log tmp/pids tmp/cache tmp/sockets public/system]

set :keep_releases, 5

set :puma_systemctl_user, :system

production.rb

# config/deploy/production.rb

server 'example.de', user: 'deploy', roles: %w[app db web]

Deployment ausführen

cap production deploy

Manuelles Deployment

#!/bin/bash
# /var/www/myapp/deploy.sh

set -e

cd /var/www/myapp

git pull origin main

bundle install --deployment --without development test

RAILS_ENV=production rails db:migrate
RAILS_ENV=production rails assets:precompile

sudo systemctl restart puma-myapp
sudo systemctl restart sidekiq-myapp

echo "Deployment abgeschlossen"

Monitoring

Health Check Endpoint

# config/routes.rb
get '/health', to: proc { [200, {}, ['OK']] }

Logs

# Puma
tail -f /var/log/puma/myapp.stdout.log

# Rails
tail -f /var/www/myapp/log/production.log

# Sidekiq
journalctl -u sidekiq-myapp -f

Zusammenfassung

| Komponente | Funktion | |------------|----------| | Ruby/rbenv | Runtime | | Rails | Framework | | Puma | Application Server | | Nginx | Reverse Proxy | | PostgreSQL | Datenbank | | Sidekiq | Background Jobs |

| Befehl | Funktion | |--------|----------| | rails db:migrate | Migrationen | | rails assets:precompile | Assets kompilieren | | cap production deploy | Capistrano Deploy |

| Datei | Funktion | |-------|----------| | config/puma.rb | Puma-Config | | config/database.yml | DB-Config | | .env | Umgebungsvariablen |

Fazit

Ruby on Rails bietet ein vollständiges Framework für Webentwicklung. Puma ist der Standard-Application-Server für Rails. Mit Capistrano lassen sich Deployments automatisieren. Sidekiq übernimmt Background Jobs zuverlässig. Die Asset-Pipeline optimiert Frontend-Ressourcen. Rails bleibt eine produktive Wahl für komplexe Webanwendungen.