Standard Setup Guide for Self-Hosted

This guide is for setting up Hudu with a free LetsEncrypt SSL certificate that will renew automatically for you. If you are looking to use a custom SSL certificate, you may want to read this article for alternative options: Getting Started with Hudu Self-Hosted.

    This standard, LetsEncrypt method, is Hudu's recommended setup method for self-hosting.

Prerequisites

In order to self-host Hudu, you will need:

  • Server with at least 4GB memory and at least 1 CPU (as long as there is no contention), with Ubuntu (22.04 LTS, or newer) as the OS.
  • SMTP server for sending outgoing mail.
  • An A record pointed to the Public IP of the instance. Usually something like docs.mywebsite.com or hudu.mywebsite.com
  • Ports 443 and 80 opened externally (required for LetsEncrypt).

Guides

Video Instructions

Would you prefer a video walk-through of the setup? 

   Please note! In the video, DigitalOcean is used for the setup; if you'd like to use a different hosted managed service provider, you may need to modify some steps.

 

Instructions

  • Test that ports 443 and 80 are opened for your publicly accessible A record.
    • Enter your URL to this tool. If both ports aren't showing as open, then you will need to fix that first.
  • SSH into the server you are wishing to host Hudu on.
  • Install Docker CE on the server. Setup instructions can be found here. Do not use SNAP to install Docker - this will cause issues later on.
  • Create a blank directory named hudu2 on the home directory. On Ubuntu, you can do this by typing:
    mkdir ~/hudu2
  • Move into the directory. On Ubuntu:
cd ~/hudu2
  • Place a file named docker-compose.yml in the directory.
    Expand to copy the content for the docker-compose.yml file
    volumes: 
      postgres_data: {}
      app_data: {}
      redis_data: {}
    services:
      db: 
        image: 'postgres:17.2'
        volumes: 
          - postgres_data:/var/lib/postgresql/data
        env_file:
          - '.env'
        logging:
          driver: "json-file"
          options:
            max-file: "5"
            max-size: "10m"
        restart: unless-stopped
      redis:
        image: 'redis:latest'
        command: redis-server
        volumes:
          - redis_data:/var/lib/redis/data
        restart: unless-stopped
      app: 
        image: hududocker/hudu:latest
        env_file:
          - '.env'
        volumes:
          - app_data:/var/www/hudu2/public/uploads/
          - app_data:/var/www/hudu2/uploads/ 
          - app_data:/var/lib/app/data
        depends_on: 
          - db
          - redis
        logging:
          driver: "json-file"
          options:
            max-file: "5"
            max-size: "100m"
        restart: unless-stopped
      worker:
        depends_on:
          - db
          - redis
        image: hududocker/hudu:latest
        command: bundle exec sidekiq -C config/sidekiq.yml
        volumes:
          - app_data:/var/www/hudu2/public/uploads/
          - app_data:/var/www/hudu2/uploads/
          - '.:/app'
        env_file:
          - '.env'
        logging:
          driver: "json-file"
          options:
            max-file: "5"
            max-size: "100m"
        restart: unless-stopped
      letsencrypt:
        image: lscr.io/linuxserver/swag
        container_name: letsencrypt
        cap_add:
          - NET_ADMIN
        env_file:
          - '.env'
        depends_on:
          - app
        volumes:
          - /var/www/hudu2/config:/config
        ports:
          - 443:443
          - 80:80
        restart: unless-stopped

     

        Already have a production environment and want to update your docker-compose.yml file? Please visit our Upgrade Postgres in a Production Environment article for more information!

     

  • Generate your .env file by running this command:
    cd ~/hudu2 && bash <(curl -fsSL https://raw.githubusercontent.com/Hudu-Technologies-Inc/self-hosting/refs/heads/main/hudu-env-wizard.sh)
    • Follow the prompts to provide your subdomain and root domain.
    • Choose your preferred storage method (y for S3, n for local storage). If you select S3, you will be prompted to enter your S3 configuration details. These settings can always be edited later.
    • Warning: Never edit the generated encryption keys in this file. These keys are used to decrypt passwords and two-factor authentication data. Changing them will result in permanent data loss or errors when creating, viewing, or editing passwords and when using two-factor authentication.
  •    It is critical that you store an exact copy of the .env file in a secure location. Your encryption and secure keys are located in this file, and you can lose access to passwords and more if this file is lost.

    • When both files are residing within the directory, run this command to start your Hudu instance:
      sudo docker compose up -d
    • Wait for the command to execute. You will know it is done when you can visit your domain and see a page referring to SWAG (the name of the SSL Cert Tool we use).
    • You will now need to set up an SSL certificate for your instance. Type
      sudo docker compose down
      to shut down your instance.
    • Navigate to /var/www/hudu2/config/nginx/site-confs/. With Ubuntu, you can do this by typing:
      cd /var/www/hudu2/config/nginx/site-confs/
    • Edit the file named default.conf and replace ALL of the contents with this file named default:
      Expand to copy the content for the default.conf file
      ## Version 5/14/2026 - Changelog: https://github.com/linuxserver/docker-letsencrypt/commits/master/root/defaults/default
      
      # redirect all traffic to https
      server {
        listen 80 default_server;
        listen [::]:80 default_server;
        server_name _;
        return 301 https://$host$request_uri;
      }
      
      server { 
       server_name _;
       
      listen 443 ssl; listen [::]:443 ssl; http2 on; root /var/www/hudu2/public; index index.html; include /config/nginx/ssl.conf; location = /.well-known/oauth-authorization-server { limit_except GET { deny all; } client_max_body_size 1k; #add_header Content-Type application/json; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://app:3000; } location = /.well-known/oauth-protected-resource { limit_except GET { deny all; } client_max_body_size 1k; #add_header Content-Type application/json; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://app:3000; } location /mcp { proxy_read_timeout 3600s; proxy_send_timeout 3600s; proxy_buffering off; proxy_cache off; proxy_set_header Connection ''; proxy_http_version 1.1; chunked_transfer_encoding off; gzip off; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://app:3000; } # deny requests for files that should never be accessed location ~ /\. { deny all; } location ~* ^.+\.(rb|log)$ { deny all; } location /cable { proxy_pass http://app:3000/cable; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_read_timeout 240s; proxy_send_timeout 240s; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_pass_request_headers on; proxy_buffering off; proxy_redirect off; break; }
      # send non-static file requests to the app server location / { try_files $uri @rails; } location @rails { include /config/nginx/proxy.conf; proxy_pass http://app:3000; } } # enable subdomain method reverse proxy confs include /config/nginx/proxy-confs/*.subdomain.conf; # enable proxy cache for auth proxy_cache_path cache/ keys_zone=auth_cache:10m;
    • Navigate back to the hudu2 folder. On Ubuntu:
      cd ~/hudu2
    • Start the instance again:
      sudo docker compose up -d
    • Your Hudu should now be up and running! Visit your domain to confirm. If you see a Hudu sign-up screen, it's successful. Head to the Hudu HQ Billing Portal for a license key.

    create_account.png

    • If you don't see this screen, contact support, and please provide logs:
      sudo docker compose logs
    Please paste these into something like Pastebin.com

     

    Additional Environment Options

    Do I need to configure any SSL-related environment variables?

    Answer: No. The standard setup includes built-in SSL, so variables like DISABLE_SSL and BEHIND_TLS_PROXY are not required.

    How do I increase the upload size limit?

    Answer: Use MAX_FILE_SIZE to set the upload limit in MB. For example, MAX_FILE_SIZE=250 sets the limit to 250 MB. The default is 100 MB.

    How do I use S3-compatible storage?

    Answer: Use the S3 variables when USE_LOCAL_FILESYSTEM is not set to true. Configure S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_BUCKET, S3_REGION, and optionally S3_ENDPOINT for providers like Wasabi or MinIO.

    When should I use S3_FORCE_PATH_STYLE?

    Answer: Use S3_FORCE_PATH_STYLE=true for MinIO or other S3-compatible providers that require path-style bucket access.

    When should I use S3_REMOVE_ENCRYPTION_HEADER?

    Answer: Use S3_REMOVE_ENCRYPTION_HEADER=true if your S3-compatible provider rejects AWS server-side encryption headers.

    When should I use S3_TURN_OFF_VERIFY_PEER?

    Answer: Use S3_TURN_OFF_VERIFY_PEER=true only for trusted endpoints where TLS verification must be skipped. This is not recommended for public or untrusted endpoints.

    Can I change rate limiting?

    Answer: Use RATE_LIMIT_REQUESTS to adjust the per-IP request limit and LOGIN_THROTTLE_PERIOD_SECONDS to adjust the login throttle window.

    Can I disable rate limiting?

    Answer: You can use DISABLE_RACK_ATTACK=true to disable rate limiting, but this is not recommended unless protections are handled upstream.

    How do I allow iframes or embedded content?

    Answer: Use WHITELISTED_FRAME_SRC to allow specific iframe sources. Use DISABLE_FRAME_CSP=true only if you need to allow all iframes, as this weakens security protections.

    How do I allow external media sources?

    Answer: Use WHITELIST_MEDIA_SRC with a comma-separated list of approved media sources.

    FAQ

    Reaching "SWAG" page / can't reach login page

    Answer: This typically means the default NGINX file didn’t get replaced, or the server wasn’t restarted after updating it. Run:

     

    cd ~/hudu2 && sudo docker-compose down && sudo docker-compose up -d

     

    Notes: If this does not resolve the issue:

    • Delete the existing

      default.conf

      file and use:

       

      mv default default.conf

      to rename the

      default

      file to

      default.conf

      .

       

    • You should end up with both default.conf and default.conf.sample in the directory.
      • Then run:

        cd ~/hudu2 && sudo docker-compose down && sudo docker-compose up -d

        to restart the stack.

      • After a few minutes, browsing to the URL should work.
    Was this article helpful?
    3 out of 3 found this helpful