Prerequisites
- 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-stoppedAlready 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.
-
Edit your
.envfile, add the following line, and save the file:DISABLE_SSL=true
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.
Note: If you are using DISABLE_SSL=true with an upstream TLS proxy, you should also set BEHIND_TLS_PROXY=true to ensure proper HTTPS URL behavior.
-
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/-
Create a file named proxy.conf and add the content below:
Expand to copy content for proxy.conf## Version 5/13/2026 - Changelog: proxy.conf file /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/:
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: Default file for no-ssl # redirect all traffic to https server { listen 80 default_server; listen [::]:80 default_server; server_name _; root /var/www/hudu2/public; index index.html; 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; } 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 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
- Please paste these into something like Pastebin.com
Additional Environment Options
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.
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.
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.
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.
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.
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.
Answer: Use S3_REMOVE_ENCRYPTION_HEADER=true if your S3-compatible provider rejects AWS server-side encryption headers.
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.
Answer: Use RATE_LIMIT_REQUESTS to adjust the per-IP request limit and LOGIN_THROTTLE_PERIOD_SECONDS to adjust the login throttle window.
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.
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.
Answer: Use WHITELIST_MEDIA_SRC with a comma-separated list of approved media sources.