Streamlit, docker, Nginx, ssl/https

Thanks for the amazing streamlit!

I am wondering if anyone has tried run streamlit in docker with https.

What I am thinking of is a combination of these:

Details:
I am using the

streamlit 0.56.0

In config config.toml I have set

enableCORS = false

It works fine with http but in every scenario that I tried with https I always had the notice

Please wait … connecting

displayed in my browser.

And in the console

Uncaught Error: Unsupported state transition.
State: PINGING_SERVER
Event: CONNECTION_TIMED_OUT

I have tried all the combinations I can think of, I have read all the posts in the forum having to do with nginx. I cant think of any solution.

I think you need some additional nginx config with proxy_… parameters.

I haven’t used with docker but I have gotten streamlit/nginx/https working in a Windows VM using tips from this post. I believe the following nginx config was what fixed the … connecting … timeout issue for me.

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;

Thank you for your reply, and for giving me some encouragement, agray!
Despite trying it’s not working, however. I have something like you suggest in my nginx config, which I’ll post here:
` # This file overrides default nginx HTTP settings for my.example.com

Mount this file as ā€œ/var/lib/nginx-conf/my.example.com.conf.erbā€

map $http_upgrade $connection_upgrade {
default upgrade;
ā€˜ā€™ close;
}

server {
    listen 443 ssl http2;
    server_name subdomain.example.local;
    keepalive_timeout 5;

    location / {
        proxy_pass http://127.0.0.1:8501/;
    }
    location ^~ /static {
        proxy_pass http://127.0.0.1:8501/static/;
    }
    location ^~ /healthz {
        proxy_pass http://127.0.0.1:8501/healthz;
    }

    location ^~ /vendor {
        proxy_pass http://127.0.0.1:8501/vendor;
    }
    location /stream { # most important config

        proxy_pass http://127.0.0.1:8501/stream;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 86400;
    }
 }`

Also I have that on starting up streamlit emits:
subdomain_1 | You can now view your Streamlit app in your browser. subdomain_1 | subdomain_1 | Network URL: http://172.21.0.2:8501 subdomain_1 | External URL: http://79.247.21.93:8501
So I switched all the 172.0.0.1 to 172.21.0.2 , but unfortunately it did not work either …

I forgot to say I am getting lots of these:

WebSocket connection to ā€˜ā€™ failed: WebSocket is closed before the connection is established.

My nginx server stanza looks like this. Maybe try putting the proxy_set… params outside of your location {…} stanzas.

server {
  listen       443 ssl;
  server_name  localhost;
  
  ssl_certificate C:/.../nginx/conf/certificate.pem;
  ssl_certificate_key C:/.../nginx/conf/key.pem;
  
  proxy_http_version 1.1; 
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header Host $host;

  # streamlit specific: 
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
  proxy_read_timeout 86400;
  
  location / {
    auth_basic           "Access Restricted";
    auth_basic_user_file C:/.../nginx/conf/.htpasswd;
    proxy_pass http://127.0.0.1:8501;
  }
}

I have now been able to solve it. I could do it using a specific option of the docker based solution https://github.com/SteveLTN/https-portal. In particular it allows to override nginx configuration files

The main point is it uses erb files, which allow variables, such as <%= domain.name %> which I had to use instead of http://127.0.0.1

I had to modify the one for ssl.
Here it is:

There were some specifics that had to do with the

# based on https://github.com/SteveLTN/https-portal/blob/master/examples/custom_config/nginx-conf/example.com.ssl.conf.erb
# This file overrides default nginx HTTPS settings for my.example.com
# Mount this file as "/var/lib/nginx-conf/my.example.com.ssl.conf.erb"

server {
    listen 443 ssl http2;

    # domain.name will be "my.example.com", you can also hard-code it.
    server_name <%= domain.name %>;

    ssl on;
    ssl_certificate <%= domain.chained_cert_path %>;
    ssl_certificate_key <%= domain.key_path %>;

    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_session_cache shared:SSL:50m;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA;
    ssl_prefer_server_ciphers on;

    ssl_dhparam <%= dhparam_path %>;

    location / {
        set $backend <%= domain.upstream %>;
        proxy_pass $backend;
    }
    location /stream {
        proxy_pass <%= domain.upstream %>/stream;
        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_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_read_timeout 2h;
    }
}

Unfortunately I have now hit the letsencrypt limits, but this should resolve itself in a couple of days.

@agray Thank you for your help and encouragement

@H_Felix_Wittmann Thanks for sharing the config!
I updated the config since ssl on; isn’t allowed anymore (removed it)
There is also the config included to enable basic auth:

server {
    listen 443 ssl http2;
    # domain.name will be "my.example.com", you can also hard-code it.
    server_name <%= domain.name %>;
    ssl_certificate <%= domain.chained_cert_path %>;
    ssl_certificate_key <%= domain.key_path %>;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_session_cache shared:SSL:50m;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA;
    ssl_prefer_server_ciphers on;
    ssl_dhparam <%= dhparam_path %>;

    <% if domain.basic_auth_enabled? %>
    auth_basic "Password";
    auth_basic_user_file <%= domain.htaccess_path %>;
    <% end %>

    location / {
        set $backend <%= domain.upstream %>;
        proxy_pass $backend;
    }

    location /stream {
        proxy_pass <%= domain.upstream %>/stream;
        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_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_read_timeout 2h;
    }
}

I ran successfully streamlit in docker but even though runOnsave is activated in streamlit conf, my app does not update.

Here is my nginx config :

server {

listen 80 default_server;


location /test/ {
    auth_request /auth;
    proxy_pass http://localhost:8501/;
    proxy_http_version 1.1;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $host;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 86400;
}

location ^~ /test/static {
    proxy_pass http://localhost:{{metadata['port']}}/static/;
}

location ^~ /test/healthz/ {
    proxy_pass http://localhost:{{metadata['port']}}/healthz/;
}

location ^~ /test/vendor/ {
    proxy_pass http://localhost:{{metadata['port']}}/vendor/;
}


location = /auth {
    proxy_pass http://localhost:8000/;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header X-Real-Ip $remote_addr;
    proxy_set_header X-Authorization $http_authorization;
}

}

Hello everyone…
Can any one please provide a step-by-step methodology for https:// custom-domain. com to access x.x.x.x:8501 ?
It seems most of us are struggling to get things going.

Thank You…

This solution does not work for me. Keep being redirected to 404.

I have a main site running on the / configured by certbot.

My streamlit sits on another location:

server {

    location /covidch/ {
        auth_basic  "Restricted Area";
        auth_basic_user_file /etc/nginx/.htpasswd;
        proxy_pass http://127.0.0.1:8502/;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
#        proxy_redirect off;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header X-Forwarded-Proto $scheme;
    }
#    error_page 404 /404.html;
#    error_page 500 502 503 504 /50x.html;

#    location = /50x.html {
#        root /usr/share/nginx/html;
#    }
}

I was facing the same issue while deploying my streamlitapp via my AWS EC2 instance:
http://MyIpAdress:8501 was working without any errors. However, once I tried to bring my ssl certificates within my nginx.conf file in order to be able to run https instead of http, https://MyDomainName didn’t work. Instead, it showed me ā€œPlease Waitā€¦ā€ and somehow I could see that there was an issue with the WebSocket. (http://MyIpAdress:8501 still worked tho)

Changing
proxy_set_header Connection ā€œupgradeā€;
to
proxy_set_header Connection $connection_upgrade;

fixed it for me somehow!

If this doesn’t fix it on your end, just let me know and I can share the entire nginx.conf file.

Hope this helps!

Could you please share the entire nginx.conf file, Thanks

Solution for running in docker on Plesk control panel.

  1. In docker container configuration set manual port mapping 8501->8501.
  2. For your domain do not use Docker Proxy Rules and use Additional nginx directives:
location ~ ^/.* {
	proxy_pass http://0.0.0.0:8501;
	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;
	# streamlit specific BEGIN
	proxy_http_version 1.1;
	proxy_set_header Upgrade $http_upgrade;
	proxy_set_header Connection "upgrade";
	# streamlit specific END
	# streamlit optional END
	proxy_buffering    off;
	proxy_read_timeout 86400;
	# streamlit optional END
}

Only marked parts have been modified the rest is the standard code, that would Plesk generate with Docker Proxy Rules.

No need to disable CORS.