Automation
    MariaDB
    MIT

    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

    ProjectHuginn (Ruby on Rails + DelayedJob)
    LicenseMIT
    Recommended PlanCloud VPS 2 GB / 2 vCPU (sweet spot for 20–100 agents)
    OSUbuntu 24.04 LTS
    DatabaseMariaDB (PostgreSQL also supported)
    Estimated Setup Time60–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.

    1

    Initial Server Preparation

    Update + baseline + admin user
    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_keys
    Lock down SSH (/etc/ssh/sshd_config)
    PermitRootLogin no
    PasswordAuthentication no
    PubkeyAuthentication yes
    Apply + firewall + hostname/timezone
    sshd -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 UTC
    Add swap on 2 GB tiers (cheap insurance during gem compilation)
    sudo 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.conf

    UTC is strongly recommended — scheduled agents don't care about local time, and DST transitions only confuse logs.

    2

    Install System Dependencies

    The official Huginn install guide targets older Ubuntu releases. Working list for 24.04 (noble):

    Build deps for Ruby + native gems
    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 bison

    Notes vs older guides: libgdbm-compat-dev is now required separately, python-docutils is removed (skip it), libxslt-devlibxslt1-dev, libncurses5-devlibncurses-dev.

    Node.js LTS for Rails asset compilation
    curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
    sudo apt install -y nodejs
    sudo npm install -g yarn
    3

    Install + Tune MariaDB

    Install + secure
    sudo apt install -y mariadb-server mariadb-client libmariadb-dev
    sudo mysql_secure_installation

    Set a strong root password, switch to unix_socket auth: No, remove anonymous users / test DB / disallow remote root: Yes.

    Create the Huginn DB user (in mysql shell)
    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;
    /etc/mysql/mariadb.conf.d/60-huginn.cnf (tune for 2 GB VPS)
    [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 = 2
    Apply
    sudo systemctl restart mariadb

    On a 4 GB VPS bump innodb_buffer_pool_size to 1G.

    4

    Create the huginn System User

    Unprivileged user
    sudo adduser --disabled-login --gecos 'Huginn' huginn

    Everything from this point that touches Huginn code, gems, or services runs as this user.

    5

    Install Ruby with rbenv

    Huginn master needs Ruby ≥3.2.4. rbenv lets you pin the exact patch and avoid system-Ruby conflicts.

    As the huginn user
    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 rehash

    Compile takes 5–15 minutes. Fails on libssl? Missing libssl-dev. OOMs? You skipped swap.

    6

    Clone + Configure Huginn

    Clone + checkout latest stable tag
    cd /home/huginn
    git clone https://github.com/huginn/huginn.git huginn
    cd huginn
    
    git tag --sort=-creatordate | head -5
    git checkout <latest_tag>
    Copy + lock the .env file
    cp .env.example .env
    chmod 0600 .env
    editor .env

    Walk top to bottom; the values that matter:

    Critical .env values
    # 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.com
    7

    Bundle Install + Migrate

    Install gems (10–30 min on a small VPS)
    bundle config set --local deployment 'true'
    bundle config set --local without 'development test'
    bundle install -j$(nproc)
    # 1 GB VPS? bundle install -j1
    Initialize DB + assets
    RAILS_ENV=production bundle exec rake db:create db:migrate db:seed
    RAILS_ENV=production bundle exec rake assets:precompile

    Seed creates admin user admin / password. Change immediately on first login.

    8

    systemd Units (Web + Worker)

    Exit huginn user, then back as sysadmin sudo
    exit
    /etc/systemd/system/huginn-web.service
    [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
    /etc/systemd/system/huginn-worker.service
    [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.target
    Logs + enable
    sudo 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
    /etc/logrotate.d/huginn
    /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
    }
    9

    Nginx + Let's Encrypt

    Install + site
    sudo apt install -y nginx certbot python3-certbot-nginx
    /etc/nginx/sites-available/huginn
    upstream 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;
        }
    }
    Enable + cert
    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-email

    Browse to https://huginn.example.com, log in as admin / password, then change the password.

    10

    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).

    /usr/local/sbin/huginn-backup.sh
    #!/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 -delete
    Schedule + ship off-host
    sudo 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-backup

    Layer 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-run rbenv install
    • Bundle install OOMs: add swap, or use bundle install -j1
    • 500 error after first login: assets not precompiled, or FORCE_SSL=true set without Nginx in front yet
    • Workers idle, no scheduled agents firing: huginn-worker not 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