How to: Run Apps in Syftbox
This guide explains how to run Apps in Syftbox, including common execution patterns and examples.
Understanding Syftbox Execution
Syftbox orchestrates the execution of your applications by:
- Looking through all folders within
Syftbox/apps
- For each application that has a
run.sh
script, executes it in a linear fashion - By default, repeating this process every 10 seconds
This automated execution pattern means your applications will be re-run every 10s, similar to a cronjob, unless you implement logic to control their execution frequency.
You are probably familiar by now with how a run.sh
script looks like, but here's a quick refresher:
#!/bin/sh
set -e
# Create and activate virtual environment
[ ! -d .venv ] && uv venv
. .venv/bin/activate
# Install dependencies
uv pip install -r requirements.txt
# Run the application
python3 main.py
# Cleanup
deactivate
Common Execution Patterns
Now, let's go together through a series of common patterns that might be a good fit for the Apps you want to run:
- Pattern 1: Run only once
- Pattern 2: Separate initialization and main execution
- Pattern 3: Customize execution frequency
- Pattern 4: Long-running process (service)
Pattern 1: Run Only Once
If you want your App to execute only once, you need to add logic to the run.sh to mark when it is first executed and check if it has already run in subsequent executions.
Example run.sh
:
#!/bin/sh
set -e
# Skip if already run
[ -f "./already_run.flag" ] && { echo "Already executed. Skipping."; exit 0; }
# Setup environment
[ ! -d .venv ] && uv venv
. .venv/bin/activate
uv pip install -r requirements.txt
# Run application
python3 main.py
# Mark as completed
touch "./already_run.flag"
deactivate
How it works:
- The script first checks if a file named
already_run.flag
exists in the current directory - If the flag file exists, it exits immediately
- Otherwise, it sets up the environment and runs the application as normal
- After successful execution, it creates the flag file and this flag file will prevent the script from running again in future
Pattern 2: Separate Initialization and Main Execution
For Apps where you want to run initialization once, but the main logic repeatedly according to the Syftbox re-execution paradigm:
Example run.sh
:
#!/bin/sh
set -e
# Setup environment
[ ! -d .venv ] && uv venv
. .venv/bin/activate
uv pip install -r requirements.txt
# Run initialization once
if [ ! -f "./init_complete.flag" ]; then
python3 init.py
touch "./init_complete.flag"
fi
# Always run main process
python3 main.py
deactivate
How it works:
- The script first sets up the environment, which is needed regardless of initialization status
- Checks if a file named
init_complete.flag
exists - If the flag doesn't exist, it runs the initialization script (init.py) and creates the flag file to mark initialization as complete
- Continues to always run the main application (main.py)
Pattern 3: Custom Execution Frequency
If you want to control the frequency at which your App runs, you can implement time-based checks. This can be done either in the run.sh
or within your main code. If your code is in Python, this is an example of how you can implement it:
Example Python code (main.py
):
import os
import json
from datetime import datetime, UTC
def should_run(file_path, interval=300):
"""Check if enough time has passed since last run"""
if not os.path.exists(file_path):
return True
try:
with open(file_path, 'r') as f:
last_run = datetime.fromisoformat(json.load(f)["last_run"])
except:
return True
seconds_since_last_run = (datetime.now(UTC) - last_run).total_seconds()
return seconds_since_last_run >= interval
def main():
from syftbox.lib import Client
# Settings
INTERVAL = 3600 # Run hourly
# Setup paths
client = Client.load()
output_folder = client.app_data("my_hourly_app")
timestamp_file = output_folder / "last_run.json"
# Check if it's time to run
if not should_run(timestamp_file, INTERVAL):
print(f"Too soon to run again. Waiting for {INTERVAL}s interval.")
return
# Your App logic here
# ...
# ...
print(f"Running App at {datetime.now(UTC)}")
# Update timestamp
os.makedirs(output_folder, exist_ok=True)
with open(timestamp_file, 'w') as f:
json.dump({"last_run": datetime.now(UTC).isoformat()}, f)
# Set permissions
SyftPermission.mine_with_public_read(email=client.email).ensure(output_folder)
if __name__ == "__main__":
main()
How it works:
- The
should_run()
function checks if enough time has passed since the last execution:- If the timestamp file doesn't exist, it's the first run and it proceeds
- Otherwise, it reads the last run time from the file and compares it with the current time - if enough time has passed, it proceeds with execution, otherwise skips it
- An interval of 3600 seconds (1 hour) is defined for this example, but you can easily adjust the interval by changing the INTERVAL constant
Pattern 4: Long-Running Process (Service)
For Apps that need to run as a service and stay alive:
Example run.sh
:
#!/bin/sh
set -e
# Configuration
PORT=8091
PID_FILE="pid.txt"
# Check if process is running
check_process() {
[ -n "$1" ] && ps -p "$1" > /dev/null 2>&1 &&
ps -p "$1" -o command= | grep -q "python main.py" && return 0
return 1
}
# Check for existing process
if [ -f "$PID_FILE" ] && check_process "$(cat "$PID_FILE")"; then
echo "Application already running"
exit 0
fi
# Setup environment
[ ! -d .venv ] && uv venv -p 3.9
. .venv/bin/activate
uv pip install -r requirements.txt --quiet
# Start service in background
echo "Starting service..."
nohup python3 main.py &
echo $! > "$PID_FILE"
# Verify startup
sleep 2
if check_process "$(cat "$PID_FILE")"; then
echo "Service started successfully (PID: $(cat "$PID_FILE"))"
else
echo "Failed to start service"
rm -f "$PID_FILE"
exit 1
fi
deactivate
How it works:
- This script creates and manages a long-running background service that persists between Syftbox execution cycles
- The
check_process
function verifies if a given process ID is running and is specifically our Python application - If the service is running, it simply exits without doing anything else
- Otherwise, it starts the main Python application in background mode using nohup (which keeps it running after the script exits) and stores the process ID in a file for future reference
- Verifies the service started successfully
This pattern enables you to run services like web servers, API endpoints, or monitoring processes that need to run continuously. Syftbox still attempts to execute this script every 10 seconds, but the script is designed to be idempotent - it only starts a new instance of the service if one isn't already running
Troubleshooting
If your App isn't running as expected:
- Check if your
run.sh
has execute permissions (chmod +x run.sh
) - Verify that your script is in the correct location (
Syftbox/apps/your_app_name/
) - Look for error messages in Syftbox logs
- Test your script manually to ensure it works outside the Syftbox orchestration
- Check for leftover state files that might be preventing re-execution
If you're still having issues, reach out to the Syftbox community for help and support.