Self-Hosting with Custom SSL Cert

The following is a guide for setting up Hudu with your own SSL certificate. If you are looking to use a free SSL certificate that auto-renews for you, you may want to read this article for alternative options: Getting Started with Hudu Self-Hosted.

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.

Instructions

  • 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 content for 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
      nginx:
        image: lscr.io/linuxserver/nginx
        container_name: nginx
        cap_add:
          - NET_ADMIN
        env_file: 
          - '.env'
        depends_on:
          - app
        volumes:
          - /var/www/hudu2/config:/config
        ports: 
          - 80:80
          - 443:443
        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 the command to start your Hudu instance: 

    sudo docker compose up -d
  • Now, run  to stop your Hudu instance:

    sudo docker compose down
    • Navigate to /var/www/hudu2/config/nginx/:
  • cd /var/www/hudu2/config/nginx/
  • Edit the file named proxy.conf and replace with the content below:

    Expand to copy content for proxy.conf 
    ## Version 5/14/2026 – BYOSSL Changelog: proxy.conf var/www/hudu2/config/nginx/proxy.conf
    
    client_body_buffer_size 128k;
    
    #Timeout if the real server is dead
    proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
    
    # Advanced Proxy Config
    send_timeout 5m;
    proxy_read_timeout 240;
    proxy_send_timeout 240;
    proxy_connect_timeout 240;
    
    # TLS 1.3 early data
    proxy_set_header Early-Data $ssl_early_data;
    
    # Basic Proxy Config
    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 https;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Ssl off;
    proxy_redirect  http://  $scheme://;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    #proxy_cookie_path / "/; HTTPOnly; Secure"; # enable at your own risk, may break certain apps
    proxy_cache_bypass $cookie_session;
    proxy_no_cache $cookie_session;
    proxy_buffers 32 4k;
    proxy_headers_hash_bucket_size 128;
    proxy_headers_hash_max_size 1024;
    

     

  • 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 04/27/2022 BYOSSL- 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;
      
      ssl_certificate /config/keys/bundle.crt;
      ssl_certificate_key /config/keys/xxxxxxx.huducloud.com.key;
    
      #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;
  • Obtain your SSL Certificate:

    Expand to see process for obtaining SSL certificate.

    There are several types of SSL certificates that can be used to protect your Hudu instance. In this tutorial, we will use a Domain Verification (“DV”) certificate from Comodo SSL Store: https://comodosslstore.com

    Other certificate authorities other than DV exist and the process for purchasing and installing the certificates for Hudu are very similar. 

    1. Purchase your DV certificate from the authority of your choice. During the process it will ask you to paste your csr. Typically, the authority will ask you what your webserver is. Hudu uses NGINX, which is not listed at Comodo, and you will have to answer OTHER.

    2. Generate your CSR on the Hudu host, as follows:

    a. Login to your Hudu host. Go into the /var/www/hudu2/config/keys folder. Depending on how you installed, you may need to create the keys folder.

    b. Run the following command to generate your CSR. Replace "server" with the domain name you intend to secure:

    openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr

    c. Enter the following CSR details when prompted:

    1. Common Name: The FQDN (fully-qualified domain name) you want to secure with the certificate such as www.google.com, secure.website.org, *.domain.net, etc.

    2. Organization: The full legal name of your organization including the corporate identifier.

    3. Organization Unit (OU): Your department such as 'Information Technology' or 'Website Security.'

    4. City or Locality: The locality or city where your organization is legally incorporated. Do not abbreviate.

    5. State or Province: The state or province where your organization is legally incorporated. Do not abbreviate.

    6. Country: The official two-letter country code (i.e. US, CH) where your organization is legally incorporated.

    3. This will create a yourdomain.csr in your keys folder. You will want to “more” this file and take a copy including the beginning and ending parts of the CSR.

    4. Paste into the authority website asking for the CSR.

    5. This process will also generate your private key, which will have an extension of .KEY

    6. Comodo will email you your certificate (others may want you to download them).

    i) This will include your intermediate Key and your public key. The intermediate certificate will be the one for NGINX.

    ii) Copy the Certificate Files into the /var/www/hudu2/config/keys folder Note: For better security, make them readable by root only.

    iii) You need to link the two certificates (or "concatenate" them) into a single file by entering the command below:

    cat your_domain_name.crt Intermediate.crt > bundle.crt

    iv) You will need to validate your keys by responding to an email. Check that you have access to the email on the domain. Typically this may be webmaster@yourdomain.com or admin@yourdomain.com. Click the validation link or copy and paste the code given.

    v) Your authority should show validated before your certificate will work.

    7. Edit your default file in /var/www/hudu2/config/nginx/sites-conf/

    i) Change the private key to your private key name (i.e., hudu.yourdomain.com.key)

    8. Restart Hudu docker by changing into the hudu2 folder:

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

    9. Your SSL certificate will now be active

  • After restarting Docker, 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 over to the Hudu HQ Billing Portal for a license key.
  • If you don't see this screen, contact support, and please provide logs:

    sudo docker compose logs

     

    Let support know if you need a secure way to send the log file!

 

Additional Environment Variables

When should I use DISABLE_SSL?

Answer: Use DISABLE_SSL=true when TLS/SSL is handled outside of Hudu, such as by Cloudflare, nginx, AWS ALB, or another reverse proxy. This disables Hudu's HTTP to HTTPS redirect on port 81.

When should I set BEHIND_TLS_PROXY?

Answer: Use BEHIND_TLS_PROXY=true together with DISABLE_SSL=true when Hudu is behind a reverse proxy that handles TLS/SSL, such as Cloudflare, nginx, AWS ALB, or another proxy.

When should I use USE_ALTERNATE_IP_CHECK?

Answer: Use USE_ALTERNATE_IP_CHECK=true if Hudu is behind a proxy and client IP addresses are not being detected correctly. This tells Hudu to use request.ip instead of request.remote_ip.

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, if needed, 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 instead of virtual-hosted-style 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 S3 endpoints where TLS certificate 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 rate limiting is handled upstream by a trusted proxy, firewall, or WAF.

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 Content Security Policy protections.

How do I allow external media sources?

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

Was this article helpful?
0 out of 0 found this helpful