diff --git a/ct/litellm.sh b/ct/litellm.sh new file mode 100644 index 0000000000..b0def16247 --- /dev/null +++ b/ct/litellm.sh @@ -0,0 +1,166 @@ +#!/usr/bin/env bash +_CS_DEFAULT_URL="https://raw.githubusercontent.com/community-scripts/DevScripts/main" +_cs_boot="${COMMUNITY_SCRIPTS_CORE_DIR:-$(dirname "${BASH_SOURCE[0]}")/../../core}/core/build.func" +source "$_cs_boot" 2>/dev/null || source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/core/build.func") +# Copyright (c) 2021-2026 community-scripts ORG +# Author: stout01 +# Co-Author: J0113 +# License: MIT | https://github.com/community-scripts/DevScripts/raw/main/LICENSE +# Source: https://github.com/BerriAI/litellm + +APP="LiteLLM" +var_tags="${var_tags:-ai;llm;proxy}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_arm64="${var_arm64:-yes}" +var_unprivileged="${var_unprivileged:-1}" +export var_litellm_http="${var_litellm_http:-yes}" +export var_litellm_version="${var_litellm_version:-1.108.0}" +export var_litellm_prisma_version="${var_litellm_prisma_version:-0.11.0}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + local venv_path="/opt/litellm/.venv" + local recovery_file="/opt/litellm/.upgrade-backup" + local upgrade_lock + exec {upgrade_lock}>/run/lock/litellm-update.lock + if ! flock -n "$upgrade_lock"; then + msg_error "A LiteLLM upgrade is already running." + exit 1 + fi + if [[ -f "$recovery_file" ]]; then + msg_error "An earlier LiteLLM upgrade needs recovery. Backups are in $(<"$recovery_file")." + exit 1 + fi + if [[ ! -x "$venv_path/bin/python" || ! -f /opt/litellm/.env || ! -f /opt/litellm/litellm.yaml || ! -f /etc/systemd/system/litellm.service ]]; then + msg_error "LiteLLM upgrades require the managed systemd installation with /opt/litellm/.env." + exit 1 + fi + + local update_url="$COMMUNITY_SCRIPTS_URL" + local current_versions current_version current_prisma_version + current_versions=$("$venv_path/bin/python" -c 'from importlib.metadata import version; print(version("litellm"), version("prisma"))') + IFS=' ' read -r current_version current_prisma_version <<<"$current_versions" + if should_upgrade "$var_litellm_version" "$current_version"; then + msg_error "LiteLLM $current_version is newer than the supported target $var_litellm_version." + exit 1 + fi + if [[ "$current_version" == "$var_litellm_version" && "$current_prisma_version" == "$var_litellm_prisma_version" ]]; then + write_update_entrypoint "${SCRIPT_SLUG:-litellm}" "litellm" "$update_url" + msg_ok "LiteLLM $current_version and Prisma $current_prisma_version are up to date." + exit 0 + fi + if ! awk ' + /^[[:space:]]*DATABASE_URL[[:space:]]*=/ { assignments++ } + /^DATABASE_URL=postgresql:\/\/litellm:[[:alnum:]]+@127\.0\.0\.1:5432\/litellm_db$/ { local_database++ } + END { exit !(assignments == 1 && local_database == 1) } + ' /opt/litellm/.env || + ! "$venv_path/bin/python" -c 'import sys, yaml; settings = (yaml.safe_load(sys.stdin) or {}).get("general_settings") or {}; sys.exit(settings.get("database_url") != "os.environ/DATABASE_URL")' /dev/null; then + PYTHON_VERSION="3.13" setup_uv + fi + + msg_info "Preparing LiteLLM $var_litellm_version with Prisma $var_litellm_prisma_version" + local new_venv schema_path + new_venv=$(mktemp -d /opt/litellm/.venv-next.XXXXXX) + if ! { + $STD uv venv --relocatable --python 3.13 "$new_venv" && + $STD uv pip install --python "$new_venv/bin/python" "litellm[proxy,extra-proxy]==$var_litellm_version" "prisma==$var_litellm_prisma_version" && + schema_path=$("$new_venv/bin/python" -c 'from importlib.util import find_spec; from pathlib import Path; print(Path(find_spec("litellm").origin).parent / "proxy" / "schema.prisma")') && + $STD env "PATH=$new_venv/bin:$PATH" "$new_venv/bin/prisma" generate --schema="$schema_path" + }; then + rm -rf -- "$new_venv" + msg_error "Could not prepare the LiteLLM upgrade." + exit 1 + fi + msg_ok "Prepared LiteLLM and Generated the Prisma Client" + + local backup_dir + backup_dir=$(mktemp -d /opt/litellm-backup.XXXXXX) + BACKUP_DIR="$backup_dir" create_backup /opt/litellm/.env /opt/litellm/litellm.yaml + cat <"$recovery_file" +$backup_dir +EOF + + msg_info "Backing Up the LiteLLM PostgreSQL Database" + systemctl stop litellm + if ! runuser -u postgres -- pg_dump --host=/var/run/postgresql --port=5432 --username=postgres --no-password --format=custom --dbname=litellm_db >"$backup_dir/postgresql.dump"; then + systemctl start litellm + rm -rf -- "$new_venv" + rm -f -- "$recovery_file" + msg_error "Could not back up the LiteLLM database. The original service has been restarted." + exit 1 + fi + msg_ok "Backed Up the LiteLLM Database to $backup_dir/postgresql.dump" + + msg_info "Starting the Upgraded LiteLLM Service" + if ! mv -T -- "$venv_path" "$backup_dir/venv"; then + systemctl start litellm + rm -rf -- "$new_venv" + rm -f -- "$recovery_file" + msg_error "Could not move the existing LiteLLM environment. The original service has been restarted." + exit 1 + fi + if ! mv -T -- "$new_venv" "$venv_path"; then + mv -T -- "$backup_dir/venv" "$venv_path" + systemctl start litellm + rm -rf -- "$new_venv" + rm -f -- "$recovery_file" + msg_error "Could not activate the new LiteLLM environment. The original service has been restarted." + exit 1 + fi + if ( + set -o pipefail + systemctl start litellm || exit 1 + deadline=$((SECONDS + 180)) + until curl -fsS --max-time 5 http://127.0.0.1:4000/health/readiness 2>/dev/null | jq -e '.db == "connected"' >/dev/null; do + ((SECONDS < deadline)) || exit 1 + sleep 2 + done + ); then + rm -f -- "$recovery_file" + write_update_entrypoint "${SCRIPT_SLUG:-litellm}" "litellm" "$update_url" + msg_ok "Upgraded LiteLLM to $var_litellm_version with Prisma $var_litellm_prisma_version" + exit 0 + fi + + systemctl stop litellm + if [[ "$current_version" == "$var_litellm_version" ]]; then + mv -T -- "$venv_path" "$new_venv" + mv -T -- "$backup_dir/venv" "$venv_path" + systemctl start litellm + rm -rf -- "$new_venv" + rm -f -- "$recovery_file" + msg_error "LiteLLM upgrade failed. The previous environment was restored. See journalctl -u litellm." + else + msg_error "LiteLLM upgrade failed and the service was stopped. The previous environment and PostgreSQL backup are in $backup_dir." + fi + exit 1 +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}LiteLLM setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW}Access it using the following URL:${CL}" +echo -e "${GATEWAY}${BGN}https://${IP}/ui${CL}" diff --git a/install/litellm-install.sh b/install/litellm-install.sh new file mode 100644 index 0000000000..05981ee217 --- /dev/null +++ b/install/litellm-install.sh @@ -0,0 +1,125 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: stout01 +# Co-Author: J0113 +# License: MIT | https://github.com/community-scripts/DevScripts/raw/main/LICENSE +# Source: https://github.com/BerriAI/litellm + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing LiteLLM Dependencies" +$STD apt install -y libsndfile1 nginx +msg_ok "Installed LiteLLM Dependencies" + +NODE_VERSION="24" NODE_COREPACK_ENABLE="0" setup_nodejs +PG_VERSION="17" setup_postgresql +PG_DB_NAME="litellm_db" +PG_DB_USER="litellm" +PG_DB_PASS="$(random_password 32)" +PG_DB_CREDS_FILE="/dev/null" setup_postgresql_db +PYTHON_VERSION="3.13" setup_uv + +msg_info "Installing LiteLLM and Generating the Prisma Client" +venv_path="/opt/litellm/.venv" +$STD uv venv --python 3.13 "$venv_path" +$STD uv pip install --python "$venv_path/bin/python" "litellm[proxy,extra-proxy]==${var_litellm_version:?LiteLLM version must be supplied by ct/litellm.sh}" "prisma==${var_litellm_prisma_version:?LiteLLM Prisma version must be supplied by ct/litellm.sh}" +schema_path=$("$venv_path/bin/python" -c 'from importlib.util import find_spec; from pathlib import Path; print(Path(find_spec("litellm").origin).parent / "proxy" / "schema.prisma")') +$STD env "PATH=$venv_path/bin:$PATH" "$venv_path/bin/prisma" generate --schema="$schema_path" +msg_ok "Installed LiteLLM and Generated the Prisma Client" + +msg_info "Configuring LiteLLM" +install -m 0600 /dev/null /opt/litellm/.env +cat </opt/litellm/.env +LITELLM_MASTER_KEY=sk-$(random_password 48) +LITELLM_SALT_KEY=sk-$(random_password 48) +DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME} +EOF +cat <<'EOF' >/opt/litellm/litellm.yaml +model_list: [] +general_settings: + master_key: os.environ/LITELLM_MASTER_KEY + database_url: os.environ/DATABASE_URL + store_model_in_db: true + trusted_proxy_ranges: ["127.0.0.1/32"] +EOF +msg_ok "Configured LiteLLM" + +msg_info "Starting LiteLLM" +cat <<'EOF' >/etc/systemd/system/litellm.service +[Unit] +Description=LiteLLM +After=network.target postgresql.service +Requires=postgresql.service + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/litellm +EnvironmentFile=/opt/litellm/.env +Environment=PATH=/opt/litellm/.venv/bin:/usr/local/bin:/usr/bin:/bin +ExecStart=/opt/litellm/.venv/bin/litellm --config /opt/litellm/litellm.yaml --host 127.0.0.1 --port 4000 +Restart=on-failure +RestartSec=5s + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now litellm + +deadline=$((SECONDS + 180)) +until health=$(curl -fsS --max-time 5 http://127.0.0.1:4000/health/readiness 2>/dev/null) && jq -e '.db == "connected"' <<<"$health" >/dev/null; do + if ((SECONDS >= deadline)); then + msg_error "LiteLLM did not become ready with PostgreSQL. Check journalctl -u litellm." + exit 1 + fi + sleep 2 +done +msg_ok "Started LiteLLM with PostgreSQL" + +create_self_signed_cert "litellm" +msg_info "Configuring the LiteLLM Web Proxy" +http_redirect=' if ($scheme = http) { return 301 https://$host$request_uri; }' +if [[ "${var_litellm_http:-yes}" == "yes" ]]; then + http_redirect="" +fi +cat </etc/nginx/sites-available/litellm +map \$http_upgrade \$litellm_connection_upgrade { + default upgrade; + '' close; +} + +server { + listen 80; + listen 443 ssl; + server_name _; + ssl_certificate /etc/ssl/litellm/litellm.crt; + ssl_certificate_key /etc/ssl/litellm/litellm.key; + client_max_body_size 0; +${http_redirect} + + location / { + proxy_pass http://127.0.0.1:4000; + proxy_http_version 1.1; + proxy_set_header Host \$host; + proxy_set_header X-Forwarded-For \$remote_addr; + proxy_set_header X-Forwarded-Proto \$scheme; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection \$litellm_connection_upgrade; + proxy_buffering off; + proxy_read_timeout 600s; + } +} +EOF +nginx_enable_site "litellm" +msg_ok "Configured the LiteLLM Web Proxy" + +motd_ssh +customize +cleanup_lxc diff --git a/json/litellm.json b/json/litellm.json new file mode 100644 index 0000000000..a33f62f495 --- /dev/null +++ b/json/litellm.json @@ -0,0 +1,57 @@ +{ + "name": "LiteLLM", + "slug": "litellm", + "categories": [ + 20 + ], + "date_created": "2026-11-08", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 443, + "documentation": "https://docs.litellm.ai/", + "website": "https://www.litellm.ai/", + "repository": "https://github.com/BerriAI/litellm", + "architectures": [ + "amd64", + "arm64" + ], + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/litellm-light.webp", + "description": "LiteLLM is an OpenAI-compatible gateway for multiple LLM providers with virtual API keys, usage tracking, and spending budgets.", + "install_methods": [ + { + "type": "default", + "script": "ct/litellm.sh", + "config_path": "/opt/litellm/litellm.yaml", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 8, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": "admin", + "password": null + }, + "app_vars": [ + { + "name": "var_litellm_http", + "label": "Allow plain HTTP on port 80", + "type": "boolean", + "default": "yes" + } + ], + "notes": [ + { + "text": "Sign in as admin using LITELLM_MASTER_KEY from /opt/litellm/.env. HTTPS uses a self-signed certificate that you must accept in your browser.", + "type": "info" + }, + { + "text": "HTTP on port 80 is enabled by default for local access and sends credentials unencrypted. Set var_litellm_http=no at install time to redirect HTTP to HTTPS.", + "type": "warning" + } + ] +}