Deploy Huginn on a VPS
A self-hosted Zapier/IFTTT alternative — agents that monitor sites, parse feeds, scrape data, post to APIs, and chain workflows. Production setup with rbenv Ruby, MariaDB, Nginx + Let's Encrypt, and systemd.
At a Glance
| Project | Huginn (Ruby on Rails + DelayedJob) |
| License | MIT |
| Recommended Plan | Cloud VPS 2 GB / 2 vCPU (sweet spot for 20–100 agents) |
| OS | Ubuntu 24.04 LTS |
| Database | MariaDB (PostgreSQL also supported) |
| Estimated Setup Time | 60–90 minutes (Ruby compile takes most of it) |
Sizing
- Personal, <20 agents: 1 vCPU / 1 GB / 20 GB SSD (add 1 GB swap)
- Personal, 20–100 agents: 2 vCPU / 2 GB / 30 GB SSD (sweet spot)
- Power user, 100–500 agents: 2–4 vCPU / 4 GB / 50 GB SSD
- Heavy scraping: 4+ vCPU / 8+ GB / 80+ GB NVMe (consider Postgres)
Each extra ~300 MB of RAM lets you run one more DelayedJob worker. Event history grows over time — busy instances accumulate gigabytes per month.
Initial Server Preparation
apt update && apt upgrade -y
apt install -y curl wget git vim ufw fail2ban htop
adduser sysadmin
usermod -aG sudo sysadmin
mkdir -p /home/sysadmin/.ssh
cp /root/.ssh/authorized_keys /home/sysadmin/.ssh/
chown -R sysadmin:sysadmin /home/sysadmin/.ssh
chmod 700 /home/sysadmin/.ssh
chmod 600 /home/sysadmin/.ssh/authorized_keysPermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yessshd -t && systemctl reload ssh
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo hostnamectl set-hostname huginn.example.com
sudo timedatectl set-timezone UTCsudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
sudo sysctl vm.swappiness=10
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.confUTC is strongly recommended — scheduled agents don't care about local time, and DST transitions only confuse logs.
Install System Dependencies
The official Huginn install guide targets older Ubuntu releases. Working list for 24.04 (noble):
sudo apt install -y \
build-essential git zlib1g-dev libyaml-dev libssl-dev \
libgdbm-dev libgdbm-compat-dev libreadline-dev libncurses-dev \
libffi-dev curl openssh-server libxml2-dev libxslt1-dev \
libcurl4-openssl-dev libicu-dev logrotate pkg-config cmake \
graphviz libpq-dev libjemalloc-dev autoconf bisonNotes vs older guides: libgdbm-compat-dev is now required separately, python-docutils is removed (skip it), libxslt-dev → libxslt1-dev, libncurses5-dev → libncurses-dev.
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt install -y nodejs
sudo npm install -g yarnInstall + Tune MariaDB
sudo apt install -y mariadb-server mariadb-client libmariadb-dev
sudo mysql_secure_installationSet a strong root password, switch to unix_socket auth: No, remove anonymous users / test DB / disallow remote root: Yes.
CREATE USER 'huginn'@'localhost' IDENTIFIED BY 'CHANGE_THIS_TO_A_STRONG_PASSWORD';
SET default_storage_engine=INNODB;
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER,
LOCK TABLES, REFERENCES ON `huginn_production`.* TO 'huginn'@'localhost';
FLUSH PRIVILEGES;[mysqld]
innodb_buffer_pool_size = 256M
innodb_log_file_size = 64M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
max_connections = 50
table_open_cache = 400
tmp_table_size = 32M
max_heap_table_size = 32M
query_cache_type = 0
query_cache_size = 0
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2sudo systemctl restart mariadbOn a 4 GB VPS bump innodb_buffer_pool_size to 1G.
Create the huginn System User
sudo adduser --disabled-login --gecos 'Huginn' huginnEverything from this point that touches Huginn code, gems, or services runs as this user.
Install Ruby with rbenv
Huginn master needs Ruby ≥3.2.4. rbenv lets you pin the exact patch and avoid system-Ruby conflicts.
sudo -i -u huginn
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
cd ~/.rbenv && src/configure && make -C src
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
source ~/.bashrc
rbenv install 3.2.6
rbenv global 3.2.6
ruby -v
gem install bundler --no-document
rbenv rehashCompile takes 5–15 minutes. Fails on libssl? Missing libssl-dev. OOMs? You skipped swap.
Clone + Configure Huginn
cd /home/huginn
git clone https://github.com/huginn/huginn.git huginn
cd huginn
git tag --sort=-creatordate | head -5
git checkout <latest_tag>cp .env.example .env
chmod 0600 .env
editor .envWalk top to bottom; the values that matter:
# Generate fresh: ruby -rsecurerandom -e 'puts SecureRandom.hex(64)'
APP_SECRET_TOKEN=<paste-output>
DOMAIN=huginn.example.com # no protocol, no trailing slash
DATABASE_ADAPTER=mysql2
DATABASE_NAME=huginn_production
DATABASE_USERNAME=huginn
DATABASE_PASSWORD='your-mariadb-password'
DATABASE_ENCODING=utf8mb4
RAILS_ENV=production
FORCE_SSL=true
INVITATION_CODE=something-only-you-know # or 'false' to disable
# SMTP (use Postmark/Mailgun/SendGrid — VPS IPs have poor sender reputation)
SMTP_DOMAIN=example.com
SMTP_SERVER=smtp.mailgun.org
SMTP_PORT=587
SMTP_USER_NAME=postmaster@mg.example.com
SMTP_PASSWORD=your-smtp-password
SMTP_AUTHENTICATION=plain
SMTP_ENABLE_STARTTLS_AUTO=true
EMAIL_FROM_ADDRESS=huginn@example.comBundle Install + Migrate
bundle config set --local deployment 'true'
bundle config set --local without 'development test'
bundle install -j$(nproc)
# 1 GB VPS? bundle install -j1RAILS_ENV=production bundle exec rake db:create db:migrate db:seed
RAILS_ENV=production bundle exec rake assets:precompileSeed creates admin user admin / password. Change immediately on first login.
systemd Units (Web + Worker)
exit[Unit]
Description=Huginn web server
After=network.target mariadb.service
Requires=mariadb.service
[Service]
Type=simple
User=huginn
Group=huginn
WorkingDirectory=/home/huginn/huginn
Environment=RAILS_ENV=production
Environment=PATH=/home/huginn/.rbenv/shims:/home/huginn/.rbenv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
ExecStart=/home/huginn/.rbenv/shims/bundle exec unicorn -c config/unicorn.rb -E production
Restart=on-failure
RestartSec=10
StandardOutput=append:/var/log/huginn/web.log
StandardError=append:/var/log/huginn/web.error.log
[Install]
WantedBy=multi-user.target[Unit]
Description=Huginn background worker
After=network.target mariadb.service huginn-web.service
Requires=mariadb.service
[Service]
Type=simple
User=huginn
Group=huginn
WorkingDirectory=/home/huginn/huginn
Environment=RAILS_ENV=production
Environment=PATH=/home/huginn/.rbenv/shims:/home/huginn/.rbenv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
ExecStart=/home/huginn/.rbenv/shims/bundle exec rails runner bin/threaded.rb
Restart=on-failure
RestartSec=10
StandardOutput=append:/var/log/huginn/worker.log
StandardError=append:/var/log/huginn/worker.error.log
[Install]
WantedBy=multi-user.targetsudo mkdir -p /var/log/huginn
sudo chown huginn:huginn /var/log/huginn
sudo systemctl daemon-reload
sudo systemctl enable --now huginn-web huginn-worker
sudo systemctl status huginn-web huginn-worker/var/log/huginn/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 huginn huginn
sharedscripts
postrotate
systemctl reload huginn-web.service > /dev/null 2>&1 || true
endscript
}Nginx + Let's Encrypt
sudo apt install -y nginx certbot python3-certbot-nginxupstream huginn {
server unix:/home/huginn/huginn/tmp/sockets/unicorn.socket fail_timeout=0;
}
server {
listen 80;
server_name huginn.example.com;
client_max_body_size 20M;
keepalive_timeout 5;
root /home/huginn/huginn/public;
location / {
try_files $uri @huginn;
}
location @huginn {
proxy_pass http://huginn;
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;
}
}sudo ln -s /etc/nginx/sites-available/huginn /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d huginn.example.com \
--redirect --agree-tos -m you@example.com --no-eff-emailBrowse to https://huginn.example.com, log in as admin / password, then change the password.
Backups
Three things matter: the MariaDB DB (every agent + event), .env (the APP_SECRET_TOKEN ties together encrypted credentials), and /home/huginn/huginn/Gemfile.lock (so a restore reproduces the same gem versions).
#!/bin/bash
set -euo pipefail
DEST=/var/backups/huginn
mkdir -p "$DEST"
TS=$(date +%F)
mysqldump -u huginn -p'YOUR_DB_PASSWORD' --single-transaction \
huginn_production | gzip > "$DEST/db-$TS.sql.gz"
cp /home/huginn/huginn/.env "$DEST/env-$TS"
cp /home/huginn/huginn/Gemfile.lock "$DEST/Gemfile.lock-$TS"
find "$DEST" -type f -mtime +14 -deletesudo chmod +x /usr/local/sbin/huginn-backup.sh
sudo chmod 700 /var/backups/huginn
echo "30 3 * * * root /usr/local/sbin/huginn-backup.sh" \
| sudo tee /etc/cron.d/huginn-backupLayer restic on top to push to B2/Wasabi/another VPS. Test the restore before relying on it.
Common Issues
- Ruby compile fails on libssl: missing
libssl-dev— install and re-runrbenv install - Bundle install OOMs: add swap, or use
bundle install -j1 - 500 error after first login: assets not precompiled, or
FORCE_SSL=trueset without Nginx in front yet - Workers idle, no scheduled agents firing:
huginn-workernot running —journalctl -u huginn-worker -n 100 - SMTP errors with Mailgun "550 5.7.1": sender domain not verified — finish DKIM/SPF in Mailgun first
