1. What a One-Off Command Is
A web app does most of its work by answering requests. Some jobs do not fit that pattern. You run them once, by hand, when something changes. Creating the first admin user is one. Updating the database after you add a column is another. Fixing a batch of records with a short script is a third.
Developers call these jobs one-off commands. On your laptop, you type them into a terminal next to your code. On a server, you need a way to run the same command next to the deployed code, with the live database and the live secrets.
This guide lists the commands Python developers run most, compares how hosts handle them and shows how to run them safely against live data.
2. Commands You Will Need Most
Django
| Command | What it does |
|---|---|
| python manage.py migrate | Applies database changes from your migration files |
| python manage.py createsuperuser --noinput | Creates an admin user from environment variables |
| python manage.py check --deploy | Lists settings that are unsafe for production |
| python manage.py showmigrations | Shows which migrations have run |
| python manage.py clearsessions | Deletes expired login sessions |
| python manage.py dumpdata app.Model --output /app/data/export.json | Exports records to a file |
| python manage.py loaddata /app/data/export.json | Imports records from a file |
Flask
| Command | What it does |
|---|---|
| flask db upgrade | Applies database changes with Flask-Migrate |
| flask routes | Lists every URL your app answers |
| flask seed-products | Runs a command you wrote yourself with @app.cli.command() |
Any Python app
| Command | What it does |
|---|---|
| python scripts/fix_prices.py | Runs a script from your project |
| alembic upgrade head | Applies database changes with Alembic |
| pip list | Shows the packages and versions that are installed |
| ls -la /app/data | Lists the files your app has saved |
| python -c "import sqlite3; print(sqlite3.sqlite_version)" | Runs one line of Python |
3. How Different Hosts Run Them
Every host has an answer to this problem. The answers differ in how much setup they need and how close the command runs to your live app.
SSH into a VPS
On your own server, you log in with SSH, move to the project folder, activate the virtual environment and type the command. You have full control. You also have full responsibility. Nothing stops a typing mistake, and nothing records what you ran unless you set that up.
ssh me@203.0.113.10
cd /srv/shop && source .venv/bin/activate
python manage.py migrate
A command-line tool from the host
Heroku made this pattern popular with heroku run. The tool starts a temporary copy of your app, runs the command there and removes the copy. Other hosts offer similar tools. You install the tool, log in from your terminal and learn its options.
A console in the browser
Some hosts, such as PythonAnywhere, give you a Bash console in the browser. It behaves like SSH without the setup. It suits exploring, but long sessions can time out, and commands you typed last month are hard to find again.
A Run a command box
Cloudilio takes the simplest route. Each app has a box where you type one command and press Run. The command runs in a fresh, temporary copy of your app, and the page shows what it printed. There is no tool to install and no key to manage. Every command is saved with its output, the time and the person who ran it.
4. Why Commands Cannot Ask Questions
On your laptop, python manage.py createsuperuser asks for a username, an email and a password. On most hosts, a one-off command has no keyboard attached. A command that waits for an answer fails or waits until its time runs out.
The fix is to give every answer up front. Most tools have an option for this.
| Tool | Option that skips the questions |
|---|---|
| Django commands | --noinput |
| pip uninstall | --yes |
| Your own scripts | Read values from options or environment variables, never from input() |
Django's createsuperuser is the most common example. With --noinput, it reads the password from an environment variable named DJANGO_SUPERUSER_PASSWORD. Save that value as a secret first, then run the command.
python manage.py createsuperuser --noinput --username admin --email you@example.com
Delete the secret after the user exists. Then sign in and change the password from the admin site.
5. Run a Command on Cloudilio
- Open your app's Tasks page. The Run a command box is at the top. Your app must be published first, because the command runs in the published version.
- Type the command. Write it the way you would in a terminal, such as
python manage.py migrate. The buttons under the box fill in common commands for your framework. - Pick a time limit. The choices are 1 minute, 5 minutes, 15 minutes and 1 hour. Cloudilio stops the command when the time is up.
- Press Run. The page shows a spinner while the command runs and updates when it finishes.
- Read the result. You see everything the command printed, how long it took and whether it finished or failed. A failed command also shows its exit code.
A few facts explain how the command behaves.
- It runs beside your app, not inside it. Cloudilio starts a temporary copy of your app for the command and removes the copy afterwards. Your website keeps answering visitors the whole time.
- It sees the same things your app sees. The copy has your code, your packages, your secrets and your data folder at
/app/data. A migration changes the same database your site reads. - Only the data folder keeps changes. A file the command writes to
/app/datastays. A file it writes anywhere else disappears with the temporary copy. - One command runs at a time for each app. This stops two migrations from running at once.
- Each app keeps its last 20 commands. Open Earlier commands to read the output of any of them again.
Chain two commands
The box runs one program. It does not understand shell symbols such as &&, | or >. To use them, hand the whole line to a shell with sh -c.
sh -c "python manage.py migrate && python manage.py clearsessions"
6. Command, Publish Step, Schedule or Worker
Cloudilio has four ways to run code outside a web request. Each one answers a different question about timing.
| Tool | When it runs | Good for |
|---|---|---|
| Run a command | Once, when you press Run | Creating an admin user, a one-time data fix, checking something |
| Run before each publish | Every time you publish, before the new version goes live | Database migrations that must match the new code |
| Scheduled task | On a timetable, such as every night | Reports, cleanups, backups to another service |
| Background worker | All the time, beside your website | Slow jobs your pages hand off, such as sending email |
A good rule for migrations is to put python manage.py migrate --noinput in Run before each publish. Then the database always matches the code. Keep Run a command for jobs that happen once or rarely.
7. Six Habits for Safe Commands
A one-off command works on live data. These habits prevent most accidents.
- Make a backup first. Before a command that changes or deletes data, create a backup on the Data & backups page. A restore takes less time than rebuilding lost records.
- Print before you change. Give data-fix scripts a mode that only prints what they would do. Run that mode first and read the output.
- Write scripts you can run twice. A script that stops halfway should be safe to start again. Check whether each record still needs the change before you change it.
- Keep secrets out of the command. The command is saved in the history. Put passwords and API keys on the Secrets page and read them from environment variables.
- Choose a time limit that fits. A check should finish in a minute. If it runs longer, something is wrong, and a short limit stops it early.
- Print progress. A script that prints a line for every thousand records shows how far it got if it fails.
This small script follows all six habits. It lowers prices by ten percent, once, and reports what it did.
import os
import sqlite3
import sys
dry_run = "--apply" not in sys.argv
db = sqlite3.connect(os.path.join(os.environ["APP_DATA_DIR"], "shop.db"))
rows = db.execute("SELECT id, price FROM products WHERE discounted = 0").fetchall()
print(f"{len(rows)} products need the discount")
for number, (product_id, price) in enumerate(rows, start=1):
if not dry_run:
db.execute(
"UPDATE products SET price = ?, discounted = 1 WHERE id = ?",
(round(price * 0.9, 2), product_id),
)
if number % 1000 == 0:
print(f"{number} done")
db.commit()
print("Dry run, nothing changed" if dry_run else "Finished")
Run python fix_prices.py to see the count. Run python fix_prices.py --apply to make the change. A second run finds nothing left to do.
8. Questions People Ask
How do I run Django migrations without SSH?
Use your host's one-off command feature. On Cloudilio, open the Tasks page, type python manage.py migrate under Run a command and press Run. To run migrations with every publish, put the same command in Run before each publish.
How do I create a Django superuser on a host with no terminal?
Save DJANGO_SUPERUSER_PASSWORD as a secret. Then run python manage.py createsuperuser --noinput --username admin --email you@example.com as a one-off command. Delete the secret afterwards.
Does a one-off command slow down my site?
It runs in its own temporary copy with its own memory limit, so it does not take memory from your website. A command that writes to the database for a long time can make pages wait, so keep heavy writes in small batches.
Can I open a Python shell?
Not an interactive one, because the command has no keyboard attached. Run a single line with python -c "...", or put the code in a script and run the script.
What happens if a command runs out of time?
Cloudilio stops it and marks it as stopped. You still see everything it printed up to that point. Choose a longer limit, up to one hour, or split the work into smaller steps.
Cloudilio Engineering Team
Application Delivery Group • CloudilioCloudilio hosts Python apps. You publish Flask, Django, FastAPI, Streamlit and plain Python scripts without managing a server, and your app's data stays safe between updates.