Ultimate Rails Setup on Debian Trixie

Aurélien Malisart

October 16, 2025

Assumptions

  • Your app is called example.
  • Your domain name is example.com.

Disable SSH password authentication

Edit /etc/ssh/sshd_config dans set (if not already):

PasswordAuthentication no

Make sure your public key has already been added by your provider:

cat .ssh/authorized_keys

Then restart SSH:

systemctl restart ssh

Create a deploy user

Create the user :

adduser deploy

Use a generated password and store it somewhere.

Then, create SSH keys for the created user (no passphrase):

su deploy
cd ~
ssh-keygen -t rsa

Upload the public key to Github/BitBucket/GitLab:

cat .ssh/id_rsa.pub
exit

Setup public key authentication for deploy too

cd /root
cp /root/.ssh/authorized_keys /home/deploy/.ssh/authorized_keys
chown deploy:deploy /home/deploy/.ssh/authorized_keys

You should be able the SSH without password for root and deploy now.

Base setup

apt-get update
apt-get upgrade
apt install zlib1g zlib1g-dev build-essential git-core curl imagemagick \
nginx libssl-dev libreadline-dev logrotate libcurl4-gnutls-dev libxml2 \
libxml2-dev libxslt1-dev ruby-dev postgresql libpq-dev libmagickcore-dev \
libmagickwand-dev libyaml-dev btop libffi-dev libyaml-dev libgdbm-dev libncurses5-dev

Mise

Install mise to be able to install needed versions of Ruby, Node and Yarn.

Enable automatic security updates

unattended-upgrades

Setup firewall

iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
iptables -A INPUT -i lo -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -p icmp --icmp-type 8 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p icmp --icmp-type 0 -m state --state ESTABLISHED,RELATED -j ACCEPT

iptables -P INPUT DROP
iptables -P OUTPUT ACCEPT
iptables -P FORWARD DROP

iptables -L -n -v

Make rules persistent:

apt install iptables-persistent

Add some SWAP space (if needed)

Create a file and swap on it:

fallocate -l 4G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile

And check if its OK:

swapon -s
free

Make it persistent on /etc/fstab:

/swapfile   none    swap    sw    0   0

Install ruby, yarn and node with Mise (as deploy)

mise install

or :

mise install ruby@XXX
mise install yarn@XXX
mise install node@xxx

Install Postgres and create a database for the project

apt install postgresql postgresql-client
su - postgres
psql -U postgres -d postgres
CREATE USER deploy WITH PASSWORD 'secret';
createdb -O deploy example

Configure nginx

Passenger

Install Phusion Passenger.

Allow nginx to access deploy’s home :

chmod g+x,o+x /home/deploy

Server block

Create a file in /etc/nginx/sites-available/example :

server {
  listen 80;
  server_name example.com;

  access_log /var/log/nginx/example.access.log;
  sendfile on;
  root /home/deploy/apps/example/current/public;

  gzip on;
  gzip_disable "msie6";

  passenger_enabled on;
  passenger_ruby /home/deploy/.rbenv/shims/ruby;
  passenger_app_env production;
  passenger_friendly_error_pages off;

  client_max_body_size 300M;
}

Enable the server block:

ln -s /etc/nginx/sites-available/example /etc/nginx/sites-enabled/example
systemctl restart nginx

Sidekiq

apt install redis

Systemd service :

# /home/deploy/.config/systemd/user/sidekiq.service

[Unit]
Description=sidekiq
After=network.target

[Service]
Type=simple
WorkingDirectory=/home/deploy/example
ExecStart=/bin/bash -lc 'eval "$(mise activate bash)" && cd /home/deploy/example && bundle exec sidekiq -e production --config config/sidekiq/production.yml'
ExecReload=/usr/bin/kill -TSTP $MAINPID
Environment=MALLOC_ARENA_MAX=2
RestartSec=1
Restart=on-failure
StandardOutput=journal
StandardError=journal
SyslogIdentifier=sidekiq

[Install]
WantedBy=default.target

Enable service :

systemctl --user enable sidekiq
systemctl --user start sidekiq
systemctl --user status sidekiq
journalctl --user -fu sidekiq

Allow user services to persist after session closed

loginctl enable-linger deploy

Persist user services logs

Set the Storage directive of the [Journal] section of /etc/systemd/journald.conf to persistent (instead of auto or volatile). A reboot is needed after editing this configuration.

Rotation of logs

# /etc/logrotate.d/example

/home/deploy/apps/example/shared/log/*.log {
    daily
    missingok
    rotate 15
    compress
    delaycompress
    notifempty
    copytruncate
}

Configure HTTPs

Install Certbot as root.

apt install python3 python3-dev python3-venv libaugeas-dev gcc
python3 -m venv /opt/certbot/
/opt/certbot/bin/pip install --upgrade pip
/opt/certbot/bin/pip install certbot certbot-nginx
ln -s /opt/certbot/bin/certbot /usr/bin/certbot
certbot --nginx
echo "0 0,12 * * * root /opt/certbot/bin/python -c 'import random; import time; time.sleep(random.random() * 3600)' && certbot renew -q" | tee -a /etc/crontab > /dev/null

Vous voulez travailler avec moi ?

Je suis développeur et consultant sur des projets informatiques dans ma société, Phonoid. Avec mes amis, nous y développons des solutions sur mesure pour nos super clients. Nous travaillons également sur nos futurs produits.