本ページはプロモーションが含まれています。

Streamlitで作ったダッシュボードを常時公開したいのに、URLを開くと起動待ちの画面が出る。レンタルサーバーに上げたら動かない。そんな状況で困っている方に向けて、Streamlitを常時公開する置き場所を3つに絞って比べます。比べるのはエックスサーバー、Streamlit Community Cloud、ConoHa VPSです。2026年11月時点の月額と、起動待ち時間の測り方、契約後に詰まらない構成を、動くコード付きでまとめました。結論から言うと、条件が「ブラウザで開けば24時間すぐ表示」ならroot権限のあるVPSが基本になります。

Streamlitを24時間すぐ表示する条件と3環境の比較(2026年11月の月額)

Streamlitは「常駐するWebサーバー」である

Streamlitは、streamlit run app.py を実行するとPythonのプロセスが起動したまま待ち受けを続ける仕組みです。この「常駐」は、プログラムが終了せずに動き続けることを指します。PHPのようにアクセスのたびに実行されて終わる方式ではありません。そのため、置き場所には次の3条件が要ります。

  • 常駐プロセスを動かしてよいこと(規約と技術の両面で)
  • 任意のポートで待ち受けるか、Webサーバーから中継できること
  • アクセスが無い間も止められないこと(スリープしない)

3環境の比較表

項目

エックスサーバー(レンタルサーバー)

Streamlit Community Cloud

ConoHa VPS

月額(2026年11月時点)

スタンダード12ヶ月契約で通常990円〜(税込)。10日間の無料お試しあり

今回の調査では確認できませんでした。公式ページで確認してください

今回の調査では確認できませんでした。公式の料金ページで確認してください

常駐プロセス

不可

サービス側が起動を管理する

可(root権限あり)

無操作時の挙動

Streamlit自体が動かせない

スリープする。次のアクセスで起動待ちが発生

自分で止めない限り動き続ける

cron

使える前提で検討できる(データ取得側に使う)

アプリ側からは使わない前提で考える

自由に設定できる

「24時間すぐ表示」に向くか

向かない

スリープがある以上、保証できない

向く

ConoHa VPS以外にroot権限のあるVPSを探す場合は、次の2つも料金の目安になります。どちらも調査で確認できた2026年11月時点の数値です。

  • Xserver VPS:2026年9月1日に全プランの料金を改定しました。2GBプランは36ヶ月契約で月額1,265円(税込、3コア・メモリ2GB)です。2GBプランは一時新規受付を止めていましたが、2026年10月6日に再開しています。
  • さくらのVPS:月額643円〜(税込、石狩リージョン、1ヶ月払い)。12ヶ月一括契約なら月額590円〜です。メモリは512MBから32GBまで7プランあります。最低利用期間は3ヶ月で、スケールダウン(下位プランへの変更)はできません。

エックスサーバーのような共有レンタルサーバーで常駐できない理由と、Webhookのような待ち受け処理の置き場所については、LINE Webhook エックスサーバーかVPSか|26年11月でも同じ観点で整理しています。

起動待ち時間はPlaywrightで「表示されるまで」を測る

HTTPの応答時間では足りない理由

Streamlitのページは、最初に空のHTMLを返し、その後にブラウザとサーバーが通信して中身を描画します。requests で測る応答時間は最初のHTMLが返るまでなので、「グラフが見えるまで」の時間とは一致しません。スリープ中のアプリでは、起動待ちの画面が先に返ることもあります。そのため私は、ブラウザ自動操作ライブラリのPlaywrightを使っています。画面に特定の文字が出るまでを測る方法です。

なお、起動待ち時間はアプリの重さ、データ量、プラン、混雑によって大きく変わります。この記事では特定の環境の数値は載せません。以下のスクリプトで、ご自分のアプリを3つの環境に置いて測ってください。

実行環境

  • Python 3.11以上を想定しています(python3 --version で確認)。Streamlitが対応するPythonのバージョンは、PyPIのページで確認してください。
  • パッケージは pip install streamlit pandas requests beautifulsoup4 playwright で入れます。Playwrightは続けて playwright install chromium でブラウザ本体も入れます。
  • バージョンは記事では固定しません。動いた時点で pip show streamlit などで確認し、pip freeze > requirements.txt で記録しておきます。サーバー側を同じ版にそろえるためです。手元とVPSで版が違うと、非推奨になった書き方が片方でだけ警告になる、といったずれが起きます。

測定スクリプト

import csv
import sys
import time
from datetime import datetime

from playwright.sync_api import sync_playwright, TimeoutError as PWTimeout

TARGETS = {
    "community_cloud": "https://username-dashboard.example.com/",
    "vps": "https://dashboard.example.com/",
}
MARK = "売上ダッシュボード"   # アプリのタイトルなど、描画後に必ず出る文字
TIMEOUT_MS = 120_000


def measure(url: str) -> float | None:
    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_page()
        start = time.perf_counter()
        try:
            page.goto(url, wait_until="domcontentloaded", timeout=TIMEOUT_MS)
            page.get_by_text(MARK).first.wait_for(timeout=TIMEOUT_MS)
            elapsed = time.perf_counter() - start
        except PWTimeout:
            elapsed = None   # 時間内に表示されなかった
        finally:
            browser.close()
    return elapsed


if __name__ == "__main__":
    label = sys.argv[1] if len(sys.argv) > 1 else "manual"
    with open("startup_log.csv", "a", newline="", encoding="utf-8") as f:
        writer = csv.writer(f)
        for name, url in TARGETS.items():
            sec = measure(url)
            writer.writerow([datetime.now().isoformat(timespec="seconds"), label, name,
                             "" if sec is None else f"{sec:.1f}"])

ブロックごとの役割は次のとおりです。

  • TARGETS / MARK:測る先のURLと、「表示された」と判定する文字を指定します。st.title() で出している文字列を使うと確実です。
  • measure():ブラウザを毎回新しく起動します。キャッシュの影響を減らすためです。ページを開き、MARKの文字が画面に出るまでの秒数を返します。2分以内に出なければ None を返します。ページがiframe(ページ内に別ページを埋め込む仕組み)で包まれていて文字を拾えない場合は、page.frame_locator() で対象を絞ってください。
  • __main__:引数で「放置後」「直後」などのラベルを付けます。結果は startup_log.csv に追記します。

測り方と判定

  1. 半日以上誰も開かなかった後に python measure.py cold を実行する(放置後の初回表示)
  2. 続けて python measure.py warm を実行する(起動済みの状態)
  3. これを数日、時間帯を変えて繰り返す。自分のアプリでも、数分おきに叩くような測り方はしない

判定の基準は単純です。「cold と warm の差が大きい」「cold で空欄(タイムアウト)が出る」なら、その置き場所は「24時間すぐ表示」の条件を満たしません。VPSに常駐させた場合は、プロセスが止まっていない限り cold と warm の差が小さくなるはずです。差が大きければ、アプリ側のデータ読み込みが重いことを疑います。

ConoHa VPSで常駐させる書き方:nohupからsystemdへ

以前の書き方

以前の私は、SSHでログインして次のように起動していました。

nohup streamlit run app.py &

これはログアウトしても動き続けます。ただし、サーバーの再起動やプロセスの異常終了で止まると、誰も起こしません。メンテナンス後に気付くと、ダッシュボードが何日も落ちていた、ということが起きます。

今の書き方:systemdのサービスにする

今は、OSの起動と同時にサービスを立ち上げ、落ちたら自動で再起動する仕組み(systemd)に登録します。root権限が要るので、VPSだからこそできる書き方です。/etc/systemd/system/dashboard.service を作ります。

[Unit]
Description=Streamlit dashboard
After=network-online.target
Wants=network-online.target

[Service]
User=username
WorkingDirectory=/home/username/dashboard
ExecStart=/home/username/dashboard/.venv/bin/streamlit run app.py --server.port 8501 --server.headless true
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
  • [Unit]:ネットワークが使える状態になってから起動します。
  • [Service]:一般ユーザー username の権限で、仮想環境(.venv)の中のstreamlitを実行します。--server.headless true は、サーバー上でブラウザを開こうとしない指定です。Restart=always と RestartSec=5 で、落ちたら5秒後に起動し直します。
  • [Install]:OS起動時に自動で立ち上げる対象に入れます。

オプション名は版によって変わることがあります。登録前に streamlit run --help と公式ドキュメントで確認してください。登録と確認は次のとおりです。

sudo systemctl daemon-reload
sudo systemctl enable --now dashboard
systemctl status dashboard
journalctl -u dashboard -n 50

status には使用メモリも出ます。さくらのVPSの512MBのような小さいプランを検討している場合は、ここで実際の使用量を見てから決めてください。さくらのVPSはスケールダウンできないので、小さく始めて上げる順番が安全です。

アプリ側:st.cacheからst.cache_dataへ

ダッシュボード本体も、以前の書き方のままだと警告が出ます。以前はデータ読み込みを @st.cache(allow_output_mutation=True) で包んでいました。st.cache は非推奨になったので、今はデータ用の st.cache_data を使います。どの版から非推奨か、削除済みかは、手元の版を pip show streamlit で確かめたうえで公式の変更履歴を参照してください。

import pandas as pd
import streamlit as st

st.set_page_config(page_title="売上ダッシュボード", layout="wide")


@st.cache_data(ttl=600)
def load(path: str) -> pd.DataFrame:
    return pd.read_csv(path, parse_dates=["date"])


st.title("売上ダッシュボード")
df = load("data/daily.csv")
st.line_chart(df, x="date", y="value")
st.dataframe(df.tail(30))
  • @st.cache_data(ttl=600):CSVの読み込み結果を600秒保持します。アクセスのたびにファイルを読み直さないので、表示が速くなります。データは後述のcronで1日1回更新するため、10分の遅れは問題になりません。
  • st.title / line_chart / dataframe:タイトル、推移グラフ、直近30行の表を出します。タイトルの文字列が、先ほどの測定スクリプトのMARKに対応します。

公開前に決めること

  • 8501番を直接外に開けず、Webサーバー(nginxなど)を前に置いてHTTPSで中継する
  • Streamlitは標準ではログイン機能が無い。URLを知る人なら誰でも見える前提で、載せるデータを選ぶ
  • SSHはパスワードではなく鍵認証にし、OSの更新を定期的にかける

2026年10月には、内閣官房国家サイバー統括室、総務省、経済産業省、IPAなど複数の機関が注意喚起を出しています。不正アクセスによる情報漏えいが相次いだことを受け、ウェブシステムの適切な管理や多要素認証の導入を求める内容です。VPSは自由と引き換えに、この管理が自分の仕事になります。

データ更新はcronで分ける:取得先サイトへの配慮込みの書き方

表示と取得を別プロセスにする

ダッシュボードの中でスクレイピングを走らせると、表示が遅くなります。アクセスが重なれば、取得先にも同じリクエストが何度も飛びます。私は、取得は1日1回のcronで動かしてCSVに書き出し、Streamlitはそれを読むだけにしています。この形なら、取得側だけをレンタルサーバーのcronに置くこともできます。レンタルサーバーでcronからPythonを動かす手順は、レンタルサーバーcron 5分おきPython実行|26年11月にまとめています。

取得スクリプト

import os
import time
import urllib.robotparser

import pandas as pd
import requests
from bs4 import BeautifulSoup

BASE = "https://example.com"
PAGES = [f"{BASE}/stats?page={i}" for i in range(1, 4)]
UA = "dashboard-fetcher/1.0 (+https://example.com/about)"
OUT = "/home/username/dashboard/data/daily.csv"

rp = urllib.robotparser.RobotFileParser(f"{BASE}/robots.txt")
rp.read()

rows = []
with requests.Session() as s:
    s.headers["User-Agent"] = UA
    for url in PAGES:
        if not rp.can_fetch(UA, url):
            continue
        res = s.get(url, timeout=30)
        res.raise_for_status()
        soup = BeautifulSoup(res.text, "html.parser")
        for tr in soup.select("table.stats tr")[1:]:
            date, value = [td.get_text(strip=True) for td in tr.select("td")[:2]]
            rows.append({"date": date, "value": int(value.replace(",", ""))})
        time.sleep(5)

tmp = OUT + ".tmp"
pd.DataFrame(rows).to_csv(tmp, index=False)
os.replace(tmp, OUT)
  • robotparser:取得先のrobots.txt(クローラーに許可・禁止する範囲を書いたファイル)を読みます。禁止されたURLは取りに行きません。
  • Session と User-Agent:接続を使い回し、誰のプログラムかが分かる名前を名乗ります。
  • time.sleep(5):1ページごとに5秒空けます。並列化はしません。
  • os.replace:一時ファイルに書いてから置き換えます。Streamlitが書きかけのCSVを読む事故を防ぐためです。

cronの設定は1日1回、早朝にします。

15 6 * * * /home/username/dashboard/.venv/bin/python /home/username/dashboard/fetch.py >> /home/username/dashboard/logs/fetch.log 2>&1

取得先への配慮で守ること

  • robots.txt:コード側で確認し、禁止範囲は取らない
  • 利用規約:自動取得や転載を禁じていないかを契約前に読む。ダッシュボードを公開するなら、取得データを第三者に見せてよいかも確認する
  • アクセス間隔:1リクエストごとに数秒空け、更新頻度は必要最小限(日次データなら1日1回)にする
  • 回避しない:ログインや回数制限がかかっているページは取得対象から外す。APIや公式のデータ提供があればそちらを使う

毎日動かし続けるには:手元のPC・レンタルサーバー・VPSが止まる条件

書いたものを毎日動かし続けるには、「表示」と「取得」のそれぞれについて、何があれば止まるかを把握しておく必要があります。

置き場所

向く役割

止まる条件

手元のPC・自宅サーバー

試作、社内だけで見る用途

電源断、スリープ、OS更新による再起動、回線やルーターの事情で外から届かない。24時間稼働の電気代は、ゲーミングPCで月2,000〜3,400円増という実測例、アイドル25Wの自作機で月約800円という試算がある

共有レンタルサーバー(エックスサーバー等)

cronでの取得処理だけ

Streamlitを常駐させられないので、表示側は置けない。cronも契約サーバーの制限内でしか動かない

Streamlit Community Cloud

人に見せる試作、表示待ちを許容できる用途

無操作時にスリープする。開いた人が起動待ちに当たる

VPS(ConoHa VPS・Xserver VPS・さくらのVPS)

常時表示と取得の両方

メモリ不足でプロセスが落ちる、ディスクが満杯になる、更新を怠って侵入される、契約更新を忘れる

条件別のおすすめ

  • 「24時間すぐ表示」が必須:VPSにsystemdで常駐させ、取得もcronで同じVPSに置く。月額の目安はさくらのVPSの643円〜からXserver VPS 2GBの1,265円(36ヶ月)の範囲。ConoHa VPSは公式の料金ページで同じメモリ量のプランを比べてください。
  • 見る人が少なく、初回の待ちを許容できる:Streamlit Community Cloudで十分です。取得処理だけは、既に契約しているレンタルサーバーのcronに置きます。
  • エックスサーバーを既に契約している:表示用には使えません。取得専用にして、表示は別の場所に置きます。

どれを選んでも、上の測定スクリプトを週1回ほど回して startup_log.csv に空欄が出ていないかを見ること、logs/fetch.log を確認することの2つが、止まったことに最短で気付く手段になります。

関連する選択肢

毎日決まった時刻にスクリプトを動かすだけなら、共有のレンタルサーバーでも足ります。cronが使えるプランを選べば、自分でOSを管理する必要はありません。

レンタルサーバー エックスサーバー

※ 広告を含みます(A8.net)。リンク経由でお申し込みがあった場合、手数料を受け取ることがあります。

Streamlit常時公開の置き場所選びのまとめ

Streamlitは常駐するWebサーバーなので、エックスサーバーのような常駐不可のレンタルサーバーには置けません。Streamlit Community Cloudは手軽ですが、無操作時にスリープするため、「開けば24時間すぐ表示」の条件は満たしません。この条件を満たすのは、root権限のあるConoHa VPSなどのVPSです。nohup ではなくsystemdの Restart=always で常駐させ、st.cache ではなく st.cache_data で読み込みを軽くします。データ取得はcronで1日1回に分け、robots.txt・利用規約・アクセス間隔を守ります。料金はXserver VPSが2026年9月の改定で2GB月額1,265円(36ヶ月)、さくらのVPSが643円〜です。起動待ちは環境ごとに違うので、Playwrightで自分のアプリを測ってから決めてください。