Apps
Apps are packages of Python code, shell scripts, requirements.txt and other config files that you deploy to and manage with Tower. You describe how to package and invoke your app to Tower using a Towerfile.
Apps can implement ETL or ELT pipelines, batch inference jobs, web scraping tasks and other common data application types. Tower executes your apps in the Tower cloud and can also run these apps locally on your development machine.
Example app
A very simple app could be structured as follows.
my-app
├── Towerfile
└── task.py
The code that the app will actually execute is in the task.py file.
cat task.py
import os
import time
count = 0
while count < 5:
count += 1
print("Hello, world!")
time.sleep(1)
The Towerfile describes how to package and invoke this app. You can learn a bit more about what goes into a Towerfile in the Towerfile reference.
cat Towerfile
[app]
name = "hello-world"
script = "./task.py"
source = [
"./task.py",
]
Versions
When you deploy an app to the Tower cloud, it generates a new immutable version (e.g., v1, v2, v3). Because every version is permanently preserved and addressable, making rollbacks and promotions safe.
Environment Binding
A version is created by being deployed to an environment. Each environment runs exactly one version at any given time, and different environments can run different versions of the same app simultaneously — for example, default is running v10 while production remains on v8.
Deploying via the CLI
Use the tower deploy command to spin up a new version. Its destination depends on the flags you include:
1. Deploy to Default
Creates a new version and targets the default environment only.
tower deploy
The resulting output of a successful deploy to your default environment looks like this:
✔ Building package... Done!
Deploying to Tower... [00:00:00] [████████████████████████████████████████] 611 B/611 B (0s)
Success! Version `v10` has been deployed to Tower!
2. Deploy to a Specific Environment
Creates a new version and targets only the specified environment.
tower deploy --environment=production
3. Deploy Everywhere
Creates a new version and immediately deploys it to every environment configured for the app.
tower deploy --all
Changing settings without re-uploading code
To change a version's run_size or parameters without packaging and uploading your code again, use the Create app version API endpoint. It creates a new version that reuses an existing version's code and applies only the settings you change. Like a deploy, it then deploys the new version to the target environment (or every environment with all_environments=true).
Omitted settings are inherited from the source version. An empty run_size resets the size to the default (basic.medium); supplying parameters replaces the full list, and [] clears it. Without source_version, the source is the target environment's current version. With all_environments=true, the source is default and the environment query parameter is ignored.
Repeating a request with the same effective code and settings reuses its existing version. Returning to an earlier combination of code and settings can therefore select an older version number. If you supply X-Tower-Idempotency-Key, reusing that key with different code or settings returns 409 Conflict.
Running tower deploy again from the same clean commit to one environment keeps that environment's currently selected version if it uses the same code bundle. This preserves both settings changes and rollbacks without copying another environment's settings. Otherwise, Tower uses the version matched by the deployment key. Deploying to all environments uses that key-matched version in every environment.
Deployment, promotion, and settings changes refresh queued scheduled first attempts only in environments whose selected version changes. Repeating an unchanged assignment leaves schedules alone; retries retain their original run settings.
Manual runs inherit the selected version's size unless a size is supplied explicitly. If the stored size is no longer available, an omitted size falls back to basic.medium. The Towerfile and MANIFEST files inside the code bundle still contain their original packaged values; use the version API to inspect the version's settings.
App-level settings such as the maximum runtime (running_timeout) aren't versioned. Change them with the Update app endpoint instead.
Next Steps: For a deep dive into full deployment, promotion, and rollback workflows, check out the Versions, environments, and promotion guide.
Parameters
To parameterize the way that applications behave at runtime, you can configure parameters in your Towerfile. When you run the app, you can pass in --parameter flags to set parameter values. When you're defining the parameter in the Towerfile, you specify a default value so you can always assume that a value will be present.
Parameters are passed in to your application's runtime environment as environment variables. You can access them using the Tower SDK — for example, if your parameter name is my_parameter, access it with tower.parameter('my_parameter').
Note: In the future, parameters will be scoped under TOWER__PARAMETER__* in the environment. For example, a parameter named MY_PARAMETER will be stored as TOWER__PARAMETER__MY_PARAMETER. Using tower.parameter('MY_PARAMETER') will continue to work — the SDK handles the resolution automatically.
Updating parameter definitions
Parameter definitions are updated every time your application is deployed.
Example app with parameters
Here's an example of the syntax for an app with two parameters defined.
cat Towerfile
[app]
name = "hello-world"
script = "./task.py"
source = [
"./task.py",
]
[[parameters]]
name = "friend"
description = "Someone that is close to you."
default = "Steve"
[[parameters]]
name = "foe"
description = "Something that you'd prefer to avoid."
default = "Carl"
And here's the example app Python for using the parameter.
import time
import tower
# This task does nothing at all, really.
count = 0
friend = tower.parameter("friend")
foe = tower.parameter("foe")
while count < 5:
count += 1
print("Hello, {friend}! Boo to {foe}".format(friend=friend, foe=foe))
time.sleep(1)
Hidden parameters
Sometimes you have sensitive data that you don't want to expose in the Tower UI or the REST API. For example, you might have an API key that your app needs to access a third-party service. You can mark parameters as "hidden" in your Towerfile to prevent them from being exposed in the UI and API.
[app]
name = "hello-world"
script = "./task.py"
source = [
"./task.py",
]
[[parameters]]
name = "friend"
description = "A secret friend that you have."
hidden = true
[[parameters]]
name = "foe"
description = "Something that you'd prefer to avoid."
default = "Carl"
Note that hidden parameters may only have empty default values, so if your app needs a non-empty value for a hidden parameter, you must pass it in when you run the app. You can pass in hidden parameters using the --parameter flag when you run the app.
When you run the app above, the value that you pass in for the friend
parameter will not be visible in the Tower UI or the REST API. However, your
app will still be able to access the value of the friend parameter using
tower.parameter("friend").
Running apps
You can use the tower run command in the terminal, the "Run App" feature in the Tower UI, or the REST API to execute (initiate a run) an app. Learn more about it in the Runs section.
More examples
You can find more examples in the tower-examples repository on GitHub.
Deployment upload limits
Deployments are subject to package-size, expanded-archive, entry-count and metadata-file limits configured by the Tower operator. Both plain TAR and gzip packages are checked; compression does not bypass the expanded-size limit. A rejected deployment does not publish a new app version. Limit errors include the applicable limit.
Uploads also have total transfer and inactivity budgets. The inactivity budget applies to each client read window; server-side processing between reads does not consume the next window. The total transfer budget still includes server processing before the body is fully read. If an upload times out, retry the deployment. Concurrent deployments to the same app are serialized; a second deploy may return a conflict while the first is still uploading. Retrying after the first finishes resolves that contention. These upload settings do not change app execution timeouts or prevent existing versions from running when an upload limit is reduced.