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;
  }
}
1 Like

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

2 Likes

@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…

5 Likes

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;
#    }
}
1 Like

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!

1 Like

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.

1 Like