A small self-hosted web queue for downloading Steam Workshop items with SteamCMD and packing them into shareable ZIP files.
Choose a configured game, paste a Steam Workshop link, and the server handles the download, archive creation, and temporary download link.
This is an unofficial project and is not affiliated with Valve or Steam. Only download and share content you are allowed to access and redistribute.
- Accepts Steam Community Workshop item links.
- Checks that the Workshop item belongs to the selected game when Steam metadata is available.
- Processes downloads one at a time through SteamCMD.
- Packs downloaded files into ZIP archives.
- Shows download progress and errors in a browser-based queue.
- Creates temporary download links and removes expired archives automatically.
- Supports an admin mode for viewing full errors and deleting queue entries.
- Runs on Windows or Linux with no separate database server.
- Python 3.11 or newer from python.org.
- SteamCMD.
- A Steam account that can access the games and Workshop items you configure.
- Enough free disk space for SteamCMD's Workshop cache and generated ZIP files.
The Python dependencies are small: Flask provides the web application and Waitress runs the production HTTP server.
Install Python 3.11 or newer. During installation, enable Add Python to PATH. You can check the installation in PowerShell:
py -3 --versionIf SteamCMD/steamcmd.exe is not already included, download the
Windows SteamCMD archive
and extract it so the project looks like this:
WorkshopDownloader/
├── SteamCMD/
│ └── steamcmd.exe
├── workshop_porter/
├── config.example.toml
├── main.py
└── start_windows.bat
SteamCMD will download its remaining runtime files the first time it starts.
Run start_windows.bat once. If config.toml does not exist, the launcher
creates it from config.example.toml and asks you to edit it.
You can also create it manually:
Copy-Item config.example.toml config.tomlAt minimum, change these values:
[server]
host = "127.0.0.1"
port = 8765
public_base_url = ""
[security]
admin_token = "choose-a-long-private-password"
admin_password = "choose-a-different-admin-password"
[steam]
username = "your-steam-login"
password = "your-steam-password"
guard_code = ""Keep host = "127.0.0.1" when the application is only used on this computer
or placed behind a reverse proxy. Use 0.0.0.0 only when it must accept direct
connections from other devices.
Run:
start_windows.bat
The launcher installs or updates the Python packages and starts the application at:
http://127.0.0.1:8765
Keep the terminal window open. Press Ctrl+C to stop the server.
Install Python 3.11 or newer and SteamCMD using the instructions for your Linux distribution. Confirm both are available:
python3 --version
steamcmd +quitThen open the project directory and run:
cp config.example.toml config.toml
nano config.toml
python3 -m pip install --user -r requirements.txt
python3 main.pyThe default Linux configuration looks for steamcmd on PATH:
[steam.linux]
steamcmd_path = "steamcmd"The application listens on port 8765 unless you change [server].port.
| Setting | Purpose |
|---|---|
host |
Address Waitress listens on. Use 127.0.0.1 locally or behind a proxy. |
port |
HTTP port. The default is 8765. |
public_base_url |
Public HTTPS address used when creating download links, such as https://mods.example.com. Leave empty for local use. |
| Setting | Purpose |
|---|---|
admin_token |
Required in the page's Password field before jobs can be created or listed. |
admin_password |
Unlocks admin mode, including full error details and delete buttons. Use a different value from admin_token. |
To enter admin mode, open the following URL once:
https://your-domain.example/?admin=YOUR_ADMIN_PASSWORD
The application replaces that URL with the normal home page and stores an HTTP-only admin cookie for eight hours. Do not share the admin URL or leave it in screenshots and browser history.
| Setting | Purpose |
|---|---|
username |
Steam login name used by SteamCMD. |
password |
Steam password used by SteamCMD. |
guard_code |
Optional Steam Guard code. Leave blank unless SteamCMD requires one. |
Steam credentials are stored as plain text in config.toml. That file is
ignored by Git and must never be committed, uploaded, or shared. A separate
Steam account dedicated to this service is strongly recommended.
| Setting | Purpose |
|---|---|
data_dir |
Parent directory for application data. |
database_path |
SQLite database containing queue jobs. |
archive_dir |
Directory containing completed ZIP archives. |
file_ttl_hours |
Hours before a ZIP link expires and its archive is deleted. |
SteamCMD keeps its own Workshop cache under SteamCMD/steamapps. Expiring a ZIP
does not clear that cache.
| Setting | Purpose |
|---|---|
max_queue_size |
Maximum number of waiting jobs. |
max_archive_gb |
Maximum allowed source size before ZIP creation is stopped. |
poll_seconds |
How often the worker checks for another job. |
Each game is an individual [[games]] entry:
[[games]]
name = "XCOM 2"
app_id = 268500
enabled = trueThe app_id is the game's Steam application ID. It appears in the game's Steam
Store URL:
https://store.steampowered.com/app/268500/XCOM_2/
^^^^^^
Restart the application after editing the game list. To temporarily hide a game without deleting its entry, use:
enabled = falseOnly regular Steam Community Workshop links are accepted, for example:
https://steamcommunity.com/sharedfiles/filedetails/?id=1234567890
- Open the web page.
- Enter the configured
admin_tokenin the Password field. - Choose the game that owns the Workshop item.
- Paste the Workshop item URL.
- Select Add to queue.
- Wait for the job to move through
queued,downloading,packing, andready. - Download the ZIP or copy its temporary link.
If the same item is already queued or being processed, the application reuses the existing job instead of downloading it twice.
The included service example expects the project at /opt/WorkShopDownloader
and a Linux system user named workshop.
sudo mkdir -p /opt/WorkShopDownloader
sudo cp -a /path/to/WorkshopDownloader/. /opt/WorkShopDownloader/
sudo useradd --system --create-home --shell /usr/sbin/nologin workshop
sudo chown -R workshop:workshop /opt/WorkShopDownloader
sudo -u workshop cp /opt/WorkShopDownloader/config.example.toml /opt/WorkShopDownloader/config.toml
sudoedit /opt/WorkShopDownloader/config.toml
sudo -u workshop python3 -m pip install --user -r /opt/WorkShopDownloader/requirements.txt
sudo cp /opt/WorkShopDownloader/workshop-downloader.service.example /etc/systemd/system/workshop-downloader.service
sudo systemctl daemon-reload
sudo systemctl enable --now workshop-downloaderCheck its status:
sudo systemctl status workshop-downloaderFollow its logs:
sudo journalctl -u workshop-downloader -fIf you use a different project path or Linux user, update
workshop-downloader.service.example before installing it.
For a public installation, put the application behind an HTTPS reverse proxy such as CloudPanel, Nginx, Caddy, or Apache.
Point the proxy to:
http://127.0.0.1:8765
Then set the external address in config.toml:
[server]
host = "127.0.0.1"
port = 8765
public_base_url = "https://mods.example.com"Restart the application after changing the configuration.
config.toml Private local configuration and credentials
config.example.toml Configuration template; replace every credential
data/jobs.sqlite3 Queue database
data/archives/ Temporary ZIP downloads
SteamCMD/steamapps/ SteamCMD Workshop cache
workshop_porter/ Application source
The config.toml, data/, caches, and generated ZIP files are ignored by Git.
On Windows, confirm this file exists:
SteamCMD/steamcmd.exe
On Linux, run steamcmd +quit. If that command is unavailable, either install
SteamCMD on PATH or put its full path in [steam.linux].steamcmd_path.
- Confirm the login name and password in
config.toml. - Start SteamCMD manually once and complete any Steam Guard or account prompts.
- Update
guard_codeif SteamCMD explicitly asks for a current code. - Confirm the account owns or can access the selected game.
The selected game must match the Workshop item's Steam application ID. Choose
the correct game or add its app_id to config.toml.
Not every Workshop item can be downloaded through SteamCMD. Check that the item still exists, is public, supports the selected game, and is accessible to the configured Steam account.
Set [server].public_base_url to the complete public HTTPS address, without a
path. For example:
public_base_url = "https://mods.example.com"Expired ZIP archives are removed automatically, but the SteamCMD Workshop cache is retained. Stop the application before manually cleaning old cache content.
Before making the service public:
- Replace every example password and token.
- Use different values for
admin_tokenandadmin_password. - Confirm
config.tomlis not tracked by Git. - Use HTTPS through a reverse proxy.
- Keep port
8765private when a reverse proxy is used. - Use a dedicated Steam account with only the access the service needs.
- Back up configuration privately, never inside the public repository.