This tutorial will guide you on how to deploy a Streamlit app on a Linode server using Nginx with SSL.
Create a Linode server. In this case, we're using Ubuntu.
Access the server via the "Launch LISH Console".
Replace newuser with your desired username. Type:
sudo adduser newuser
sudo passwd newuser
sudo usermod -aG sudo newuserClose the console and re-login using your new user in the terminal:
ssh newuser@your_linode_ipInstall UFW and allow SSH, HTTP, and TCP connections on port 443.
sudo apt-get install -y ufw
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow 443/tcp
sudo ufw --force enable
sudo ufw statusInstall uv package manager using the official installer:
curl -LsSf https://astral.sh/uv/install.sh | shAdd uv to your PATH by sourcing the shell configuration:
source ~/.cargo/envOr restart your terminal session to ensure uv is available in your PATH.
Alternative installation methods:
If you prefer to install via pip:
pip install uvOr using your system package manager:
# On Ubuntu/Debian
curl -LsSf https://astral.sh/uv/install.sh | sh
# On macOS with Homebrew
brew install uv
# On Windows with pip
pip install uvInstall Nginx and Git:
sudo apt-get -y install nginx gitClone your app repository and navigate into the app folder:
git clone your_app
cd your_appInstall the required dependencies using uv sync (this will automatically create a virtual environment and install dependencies):
uv syncAlternatively, if you prefer to use requirements.txt:
uv venv
source .venv/bin/activate
uv pip install -r requirements.txtCreate .streamlit/secrets.toml and .streamlit/config.toml:
nano .streamlit/secrets.tomlSave your secrets inside this file.
Next, open the config.toml:
nano .streamlit/config.tomlAdd the following configuration:
[server]
port = 8501
headless = true
[browser]
serverAddress = "localhost"
gatherUsageStats = false
serverPort = 8501Replace your_service with your service name:
sudo nano /etc/systemd/system/your_service.serviceAdd the following configuration, replacing the paths and Python script name as needed:
Note: To find the correct path to uv, run which uv in your terminal. Common paths are:
/usr/local/bin/uv(if installed via installer script)/home/newuser/.local/bin/uv(if installed via pip with --user)/usr/bin/uv(if installed via system package manager)
[Unit]
Description=Your App
After=network.target
[Service]
ExecStart=/home/newuser/.local/bin/uv run streamlit run /home/newuser/your_app/main.py
WorkingDirectory=/home/newuser/your_app
User=newuser
Group=newuser
Restart=always
[Install]
WantedBy=multi-user.targetReload the system daemon, enable and start your service, then check its status:
sudo systemctl daemon-reload
sudo systemctl enable your_service.service
sudo systemctl start your_service.service
sudo systemctl status your_service.serviceAllow connections on port 8501, then check the UFW status:
sudo ufw allow 8501
sudo ufw statusVisit your_linode_ip:8501 in your web browser. If everything is working fine, disable the port 8501:
sudo ufw delete allow 8501
sudo ufw statusCreate a temporary SSL certificate:
# Run these commands inside your_app folder
mkdir certs
openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 -keyout certs/key.pem -out certs/cert.pemConfigure Nginx:
sudo nano /etc/nginx/sites-available/streamlitAdd the following configuration, replacing the paths, IP address, and port as needed:
upstream ws-backend {
server localhost:8501;
}
server {
listen 80;
server_name your_linode_ip;
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name your_linode_ip;
ssl_certificate /home/newuser/your_app/certs/cert.pem;
ssl_certificate_key /home/newuser/your_app/certs/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
client_max_body_size 100M;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Content-Security-Policy "default-src 'self' https: data: blob: 'unsafe-inline' 'unsafe-eval';";
location / {
proxy_pass http://ws-backend;
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_read_timeout 86400;
proxy_send_timeout 86400;
}
}
Link the configuration file, check the configuration, and reload Nginx:
sudo ln -s /etc/nginx/sites-available/streamlit /etc/nginx/sites-enabled
sudo nginx -t
sudo service nginx reloadVisit your_linode_ip in your web browser. You should get a warning; accept the risk for now.
Purchase a domain from a provider like Namecheap. Go to your dashboard on your provider's site, click "Manage", then add a new A Record with Host = @ and Value = your_linode_ip.
Back on the Linode terminal, modify the Nginx configuration:
sudo nano /etc/nginx/sites-available/streamlitChange the server_name to your domain name, then reload Nginx:
sudo service nginx reloadFollow the guide at https://certbot.eff.org/instructions to install SSL certificates.
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo certbot --nginxFollow the prompts to complete the installation.
Voilà! Your Streamlit app is now deployed on your Linode server.