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
hudu2on the home directory. On Ubuntu, you can do this by typing:mkdir ~/hudu2
- Move into the directory. On Ubuntu:
cd ~/hudu2
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!
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.
-
If you don't see this screen, contact
support,
and please provide logs:
sudo docker compose logs
Additional Environment Options
Answer: No. The standard setup includes built-in SSL, so variables like DISABLE_SSL and BEHIND_TLS_PROXY are not required.
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 optionally 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.
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 endpoints where TLS 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 protections are handled upstream.
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.
Answer: Use WHITELIST_MEDIA_SRC with a comma-separated list of approved media sources.
FAQ
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.
-