So you’ve coded a Streamlit project, pushed it to GitHub, and rented a cheap VPS. Here is how to turn that server into a public-facing web app — no prior Docker or sysadmin chops required.
1. Prep the VPS (Ubuntu 22.04)
# connect
ssh root@203.0.113.10 # replace with your real IP
# update & install basics
apt update && apt upgrade -y
apt install python3 python3-pip git nginx -y
# install docker
apt install apt-transport-https ca-certificates curl software-properties-common -y
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
apt update && apt install docker-ce -y
systemctl enable --now docker
2. Clone Your GitHub Repo
2 a. Create a Personal Access Token
- GitHub → Settings > Developer settings > Personal access tokens > Generate new token (classic)
- Select
repo
scope only → Generate - Copy the token (example
ghp_XXXXXXXXXXXXXXXX
)
2 b. Clone with the Token
# replace USER and TOKEN
git clone https://USER:TOKEN@github.com/USER/your-streamlit-repo.git /root/app
cd /root/app
(Safer: add git config --global credential.helper store
, or switch to SSH keys later.)
3. Add a Minimal Dockerfile
# Dockerfile
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8501
CMD ["streamlit","run","app.py","--server.port=8501","--server.address=0.0.0.0"]
No requirements.txt
? Auto-generate from imports:
pip install pipreqs
pipreqs /root/app --force
4. Build & Run the Container
docker build -t streamlit-app .
docker run -d -p 8501:8501 --name streamlit --restart unless-stopped streamlit-app
# quick test
curl -I http://localhost:8501 # expect 200 OK
5. Simple Nginx Proxy
nano /etc/nginx/sites-available/app.example.com
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://localhost:8501;
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-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
}
}
ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
6. Point a Cloudflare Sub-Domain
- Cloudflare → DNS → Add an A record:
app 203.0.113.10
(orange cloud) - Cloudflare → SSL/TLS → choose Flexible for quick testing (later switch to Full)
Give DNS a minute, then visit https://app.example.com
. Your Streamlit app is live.
7. Pull Updates & Redeploy
cd /root/app
git pull
docker build -t streamlit-app .
docker rm -f streamlit
docker run -d -p 8501:8501 --name streamlit --restart unless-stopped streamlit-app
Automate that block with a script or cron job.
Notes:
A. Streamlit Config Behind Proxy
.streamlit/config.toml in your repo should be:
[server]
enableCORS = false
enableXsrfProtection = false
B. Simple UFW Firewall
ufw allow 22 80 443
ufw enable
C. Optional End-to-End TLS
# switch Cloudflare to "Full (strict)"
apt install certbot python3-certbot-nginx -y
certbot --nginx -d app.example.com