Lybic Docs

Running Long-Running Asynchronous Tasks

Running long-term background tasks in Linux sandboxes using systemd

This documentation applies to Linux sandboxes. If you are using a Windows sandbox, please refer to Running Long-Running Tasks in Windows Sandboxes.

In Lybic sandboxes, you may need to run long-executing tasks such as model training, data processing, or web servers. Since the execSandboxProcess API has timeout limits and does not support background execution, we recommend using systemd --user run to execute such tasks.

Tip: For quick-running short commands (completing within 5 seconds), please use the execSandboxProcess API.

How It Works

systemd --user run creates a temporary systemd service unit (transient unit) that automatically cleans up after the task completes. The task runs under systemd's management, decoupled from the API call.

Usage

Step Overview

  1. Prepare script or program: Transfer the script/program to the sandbox
  2. Start task: Use systemd --user run to start the long-running task
  3. Check status: Query the task's running status and logs

Step 1: Prepare Script or Program

There are two ways to prepare the script or program to run:

Method A: File Copy

Upload the script to the sandbox using the File Transfer API:

# First, upload the script to object storage (get download URL)
# Then, download to sandbox from URL
curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/file/copy" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "files": [
      {
        "id": "upload-training-script",
        "src": {
          "type": "httpGetLocation",
          "url": "https://storage.example.com/scripts/train.py"
        },
        "dest": {
          "type": "sandboxFileLocation",
          "path": "/home/agent/scripts/train.py"
        }
      }
    ]
  }'
import asyncio
from lybic import LybicClient, LybicAuth
from lybic.dto import (
    SandboxFileCopyRequestDto,
    FileCopyItem,
    SandboxFileLocation,
    HttpGetLocation
)

async def main():
    async with LybicClient(
        LybicAuth(
            org_id="ORG-xxxx",
            api_key="lysk-xxxxxxxxxxx",
            endpoint="https://api.lybic.cn/"
        )
    ) as client:
        # Upload script to sandbox
        await client.sandbox.copy_files(
            "SBX-xxxx",
            SandboxFileCopyRequestDto(files=[
                FileCopyItem(
                    id="upload-training-script",
                    src=HttpGetLocation(
                        url="https://storage.example.com/scripts/train.py"
                    ),
                    dest=SandboxFileLocation(
                        path="/home/agent/scripts/train.py"
                    )
                )
            ])
        )
        print("Script uploaded successfully")

if __name__ == "__main__":
    asyncio.run(main())
import { LybicClient } from '@lybic/core'

const lybic = new LybicClient({
  baseUrl: 'https://api.lybic.cn',
  orgId: 'ORG-xxxx',
  apiKey: 'lysk-your-api-key-here',
})

// Upload script to sandbox
await lybic.copyFilesWithSandbox('SBX-xxxx', {
  files: [
    {
      id: 'upload-training-script',
      src: {
        type: 'httpGetLocation',
        url: 'https://storage.example.com/scripts/train.py',
      },
      dest: {
        type: 'sandboxFileLocation',
        path: '/home/agent/scripts/train.py',
      },
    },
  ],
})

console.log('Script uploaded successfully')
package main

import (
    "context"
    "fmt"
    "github.com/lybic/lybic-sdk-go"
)

func main() {
    ctx := context.Background()
    client, _ := lybic.NewClient(nil)

    copyDto := lybic.SandboxFileCopyRequestDto{
        Files: []lybic.SandboxFileCopyRequestDtoFiles{
            {
                Id: "upload-training-script",
                Src: map[string]string{
                    "type": "httpGetLocation",
                    "url":  "https://storage.example.com/scripts/train.py",
                },
                Dest: map[string]any{
                    "type": "sandboxFileLocation",
                    "path": "/home/agent/scripts/train.py",
                },
            },
        },
    }

    _, err := client.CopyFilesWithSandbox(ctx, "SBX-xxxx", copyDto)
    if err != nil {
        fmt.Println("Error uploading script:", err)
        return
    }
    fmt.Println("Script uploaded successfully")
}

Method B: Write via stdin

Use execSandboxProcess to pipe script content to a file:

# Base64 encode script content
SCRIPT_CONTENT=$(cat << 'EOF' | base64 -w0
#!/usr/bin/env python3
import time
import sys

print("Starting model training...")
for i in range(100):
    time.sleep(1)
    print(f"Progress: {i+1}/100")
    sys.stdout.flush()
print("Training completed!")
EOF
)

# Write to file
curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/process" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "executable": "bash",
    "args": ["-c", "cat > /home/agent/scripts/train.py && chmod +x /home/agent/scripts/train.py"],
    "stdinBase64": "'$SCRIPT_CONTENT'"
  }'
import asyncio
import base64
from lybic import LybicClient, LybicAuth

async def main():
    async with LybicClient(
        LybicAuth(
            org_id="ORG-xxxx",
            api_key="lysk-xxxxxxxxxxx",
            endpoint="https://api.lybic.cn/"
        )
    ) as client:
        # Prepare script content
        script_content = """#!/usr/bin/env python3
import time
import sys

print("Starting model training...")
for i in range(100):
    time.sleep(1)
    print(f"Progress: {i+1}/100")
    sys.stdout.flush()
print("Training completed!")
"""
        
        # Base64 encode
        stdin_data = base64.b64encode(script_content.encode()).decode()
        
        # Write to file and add execute permission
        result = await client.sandbox.execute_process(
            "SBX-xxxx",
            executable="bash",
            args=["-c", "cat > /home/agent/scripts/train.py && chmod +x /home/agent/scripts/train.py"],
            stdinBase64=stdin_data
        )
        
        if result.exitCode == 0:
            print("Script created successfully")
        else:
            print(f"Creation failed: {base64.b64decode(result.stderrBase64 or '').decode()}")

if __name__ == "__main__":
    asyncio.run(main())
import { LybicClient } from '@lybic/core'

const lybic = new LybicClient({
  baseUrl: 'https://api.lybic.cn',
  orgId: 'ORG-xxxx',
  apiKey: 'lysk-your-api-key-here',
})

// Prepare script content
const scriptContent = `#!/usr/bin/env python3
import time
import sys

print("Starting model training...")
for i in range(100):
    time.sleep(1)
    print(f"Progress: {i+1}/100")
    sys.stdout.flush()
print("Training completed!")
`

// Base64 encode
const stdinBase64 = btoa(scriptContent)

// Write to file and add execute permission
const result = await lybic.execSandboxProcess('SBX-xxxx', {
  executable: 'bash',
  args: ['-c', 'cat > /home/agent/scripts/train.py && chmod +x /home/agent/scripts/train.py'],
  stdinBase64: stdinBase64,
})

if (result.data?.exitCode === 0) {
  console.log('Script created successfully')
} else {
  console.log('Creation failed:', atob(result.data?.stderrBase64 || ''))
}
package main

import (
    "context"
    "encoding/base64"
    "fmt"
    "github.com/lybic/lybic-sdk-go"
)

func main() {
    ctx := context.Background()
    client, _ := lybic.NewClient(nil)

    // Prepare script content
    scriptContent := `#!/usr/bin/env python3
import time
import sys

print("Starting model training...")
for i in range(100):
    time.sleep(1)
    print(f"Progress: {i+1}/100")
    sys.stdout.flush()
print("Training completed!")
`

    // Base64 encode
    stdinBase64 := base64.StdEncoding.EncodeToString([]byte(scriptContent))
    
    // Write to file and add execute permission
    cmd := "cat > /home/agent/scripts/train.py && chmod +x /home/agent/scripts/train.py"
    processDto := lybic.SandboxProcessRequestDto{
        Executable:  "bash",
        Args:        []string{"-c", cmd},
        StdinBase64: &stdinBase64,
    }

    result, _ := client.ExecSandboxProcess(ctx, "SBX-xxxx", processDto)
    
    if result.ExitCode == 0 {
        fmt.Println("Script created successfully")
    } else {
        stderr, _ := base64.StdEncoding.DecodeString(result.StderrBase64)
        fmt.Printf("Creation failed: %s\n", string(stderr))
    }
}

Step 2: Start Task Using systemd --user run

Use systemd-run --user to start the long-running task. This command returns immediately while the task continues running in the background.

# Start long-running task
curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/process" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "executable": "systemd-run",
    "args": [
      "--user",
      "--unit=my-training-task",
      "--description=Model Training Task",
      "python3",
      "/home/agent/scripts/train.py"
    ]
  }'
import asyncio
import base64
from lybic import LybicClient, LybicAuth

async def main():
    async with LybicClient(
        LybicAuth(
            org_id="ORG-xxxx",
            api_key="lysk-xxxxxxxxxxx",
            endpoint="https://api.lybic.cn/"
        )
    ) as client:
        # Start systemd task
        result = await client.sandbox.execute_process(
            "SBX-xxxx",
            executable="systemd-run",
            args=[
                "--user",
                "--unit=my-training-task",
                "--description=Model Training Task",
                "python3",
                "/home/agent/scripts/train.py"
            ]
        )
        
        output = base64.b64decode(result.stdoutBase64 or '').decode()
        print(f"Task started: {output}")
        print(f"Exit code: {result.exitCode}")

if __name__ == "__main__":
    asyncio.run(main())
import { LybicClient } from '@lybic/core'

const lybic = new LybicClient({
  baseUrl: 'https://api.lybic.cn',
  orgId: 'ORG-xxxx',
  apiKey: 'lysk-your-api-key-here',
})

// Start systemd task
const result = await lybic.execSandboxProcess('SBX-xxxx', {
  executable: 'systemd-run',
  args: [
    '--user',
    '--unit=my-training-task',
    '--description=Model Training Task',
    'python3',
    '/home/agent/scripts/train.py',
  ],
})

const output = atob(result.data?.stdoutBase64 || '')
console.log('Task started:', output)
console.log('Exit code:', result.data?.exitCode)
package main

import (
    "context"
    "encoding/base64"
    "fmt"
    "github.com/lybic/lybic-sdk-go"
)

func main() {
    ctx := context.Background()
    client, _ := lybic.NewClient(nil)

    // Start systemd task
    processDto := lybic.SandboxProcessRequestDto{
        Executable: "systemd-run",
        Args: []string{
            "--user",
            "--unit=my-training-task",
            "--description=Model Training Task",
            "python3",
            "/home/agent/scripts/train.py",
        },
    }

    result, err := client.ExecSandboxProcess(ctx, "SBX-xxxx", processDto)
    if err != nil {
        fmt.Println("Error starting task:", err)
        return
    }

    stdout, _ := base64.StdEncoding.DecodeString(result.StdoutBase64)
    fmt.Printf("Task started: %s\n", string(stdout))
    fmt.Printf("Exit code: %d\n", result.ExitCode)
}

Important parameter explanations:

  • --user: Run in user mode (required)
  • --unit=<name>: Specify unit name for later status queries and log viewing
  • --description=<description>: Task description (optional)
  • Subsequent parameters are the actual command to execute

Step 3: Check Task Status

3.1 View Task Running Status

Use systemctl --user status to check task running status:

# Check task status
curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/process" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "executable": "systemctl",
    "args": ["--user", "status", "my-training-task"]
  }'
import asyncio
import base64
from lybic import LybicClient, LybicAuth

async def main():
    async with LybicClient(
        LybicAuth(
            org_id="ORG-xxxx",
            api_key="lysk-xxxxxxxxxxx",
            endpoint="https://api.lybic.cn/"
        )
    ) as client:
        # Check task status
        result = await client.sandbox.execute_process(
            "SBX-xxxx",
            executable="systemctl",
            args=["--user", "status", "my-training-task"]
        )
        
        status = base64.b64decode(result.stdoutBase64 or '').decode()
        print("Task status:")
        print(status)

if __name__ == "__main__":
    asyncio.run(main())
import { LybicClient } from '@lybic/core'

const lybic = new LybicClient({
  baseUrl: 'https://api.lybic.cn',
  orgId: 'ORG-xxxx',
  apiKey: 'lysk-your-api-key-here',
})

// Check task status
const result = await lybic.execSandboxProcess('SBX-xxxx', {
  executable: 'systemctl',
  args: ['--user', 'status', 'my-training-task'],
})

const status = atob(result.data?.stdoutBase64 || '')
console.log('Task status:')
console.log(status)
package main

import (
    "context"
    "encoding/base64"
    "fmt"
    "github.com/lybic/lybic-sdk-go"
)

func main() {
    ctx := context.Background()
    client, _ := lybic.NewClient(nil)

    // Check task status
    processDto := lybic.SandboxProcessRequestDto{
        Executable: "systemctl",
        Args:       []string{"--user", "status", "my-training-task"},
    }

    result, _ := client.ExecSandboxProcess(ctx, "SBX-xxxx", processDto)
    
    stdout, _ := base64.StdEncoding.DecodeString(result.StdoutBase64)
    fmt.Println("Task status:")
    fmt.Println(string(stdout))
}

Example status output:

● my-training-task.service - Model Training Task
     Loaded: loaded (/run/user/1000/systemd/transient/my-training-task.service; transient)
  Transient: yes
     Active: active (running) since Mon 2024-01-15 10:30:00 UTC; 5min ago
   Main PID: 12345 (python3)
      Tasks: 1 (limit: 4915)
     Memory: 45.2M
        CPU: 2.5s
     CGroup: /user.slice/user-1000.slice/user@1000.service/app.slice/my-training-task.service
             └─12345 python3 /home/agent/scripts/train.py

3.2 View Task Logs

Use journalctl --user to view task output logs:

# View task logs (latest 100 lines)
curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/process" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "executable": "journalctl",
    "args": [
      "--user",
      "--unit=my-training-task",
      "-n", "100",
      "--no-pager"
    ]
  }'
import asyncio
import base64
from lybic import LybicClient, LybicAuth

async def main():
    async with LybicClient(
        LybicAuth(
            org_id="ORG-xxxx",
            api_key="lysk-xxxxxxxxxxx",
            endpoint="https://api.lybic.cn/"
        )
    ) as client:
        # View task logs
        result = await client.sandbox.execute_process(
            "SBX-xxxx",
            executable="journalctl",
            args=[
                "--user",
                "--unit=my-training-task",
                "-n", "100",
                "--no-pager"
            ]
        )
        
        logs = base64.b64decode(result.stdoutBase64 or '').decode()
        print("Task logs:")
        print(logs)

if __name__ == "__main__":
    asyncio.run(main())
import { LybicClient } from '@lybic/core'

const lybic = new LybicClient({
  baseUrl: 'https://api.lybic.cn',
  orgId: 'ORG-xxxx',
  apiKey: 'lysk-your-api-key-here',
})

// View task logs
const result = await lybic.execSandboxProcess('SBX-xxxx', {
  executable: 'journalctl',
  args: [
    '--user',
    '--unit=my-training-task',
    '-n', '100',
    '--no-pager',
  ],
})

const logs = atob(result.data?.stdoutBase64 || '')
console.log('Task logs:')
console.log(logs)
package main

import (
    "context"
    "encoding/base64"
    "fmt"
    "github.com/lybic/lybic-sdk-go"
)

func main() {
    ctx := context.Background()
    client, _ := lybic.NewClient(nil)

    // View task logs
    processDto := lybic.SandboxProcessRequestDto{
        Executable: "journalctl",
        Args: []string{
            "--user",
            "--unit=my-training-task",
            "-n", "100",
            "--no-pager",
        },
    }

    result, _ := client.ExecSandboxProcess(ctx, "SBX-xxxx", processDto)
    
    stdout, _ := base64.StdEncoding.DecodeString(result.StdoutBase64)
    fmt.Println("Task logs:")
    fmt.Println(string(stdout))
}

Example log output:

Jan 28 06:20:37 agent-machine systemd[877]: Started my-training-task.service - Model Training Task.
Jan 28 06:20:38 agent-machine python3[17083]: Starting model training...
Jan 28 06:20:38 agent-machine python3[17083]: Progress: 1/100
Jan 28 06:20:39 agent-machine python3[17083]: Progress: 2/100
Jan 28 06:20:40 agent-machine python3[17083]: Progress: 3/100
Jan 28 06:20:41 agent-machine python3[17083]: Progress: 4/100
Jan 28 06:20:42 agent-machine python3[17083]: Progress: 5/100
...

Common journalctl parameters:

  • -n <number>: Show the latest N lines of logs
  • -f: Follow logs in real-time (not recommended due to API timeout limits)
  • --no-pager: Output directly without pagination (required)
  • --since "5 minutes ago": Show logs from the last 5 minutes
  • -o json: Output in JSON format

3.3 Check If Task Is Still Running

Use systemctl --user is-active to quickly check task status:

curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/process" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "executable": "systemctl",
    "args": ["--user", "is-active", "my-training-task"]
  }'
import asyncio
import base64
from lybic import LybicClient, LybicAuth

async def main():
    async with LybicClient(
        LybicAuth(
            org_id="ORG-xxxx",
            api_key="lysk-xxxxxxxxxxx",
            endpoint="https://api.lybic.cn/"
        )
    ) as client:
        result = await client.sandbox.execute_process(
            "SBX-xxxx",
            executable="systemctl",
            args=["--user", "is-active", "my-training-task"]
        )
        
        status = base64.b64decode(result.stdoutBase64 or '').decode().strip()
        
        if status == "active":
            print("✓ Task is running")
        elif status == "inactive":
            print("✗ Task has stopped")
        else:
            print(f"Task status: {status}")

if __name__ == "__main__":
    asyncio.run(main())
import { LybicClient } from '@lybic/core'

const lybic = new LybicClient({
  baseUrl: 'https://api.lybic.cn',
  orgId: 'ORG-xxxx',
  apiKey: 'lysk-your-api-key-here',
})

const result = await lybic.execSandboxProcess('SBX-xxxx', {
  executable: 'systemctl',
  args: ['--user', 'is-active', 'my-training-task'],
})

const status = atob(result.data?.stdoutBase64 || '').trim()

if (status === 'active') {
  console.log('✓ Task is running')
} else if (status === 'inactive') {
  console.log('✗ Task has stopped')
} else {
  console.log('Task status:', status)
}
package main

import (
    "context"
    "encoding/base64"
    "fmt"
    "strings"
    "github.com/lybic/lybic-sdk-go"
)

func main() {
    ctx := context.Background()
    client, _ := lybic.NewClient(nil)

    processDto := lybic.SandboxProcessRequestDto{
        Executable: "systemctl",
        Args:       []string{"--user", "is-active", "my-training-task"},
    }

    result, _ := client.ExecSandboxProcess(ctx, "SBX-xxxx", processDto)
    
    stdout, _ := base64.StdEncoding.DecodeString(result.StdoutBase64)
    status := strings.TrimSpace(string(stdout))
    
    if status == "active" {
        fmt.Println("✓ Task is running")
    } else if status == "inactive" {
        fmt.Println("✗ Task has stopped")
    } else {
        fmt.Printf("Task status: %s\n", status)
    }
}

Return values:

  • active: Task is running
  • inactive: Task has stopped
  • failed: Task failed

Complete Example: Running a Web Server

Below is a complete example showing how to run a long-running Python web server in a sandbox:

import asyncio
import base64
from lybic import LybicClient, LybicAuth

async def run_web_server():
    async with LybicClient(
        LybicAuth(
            org_id="ORG-xxxx",
            api_key="lysk-xxxxxxxxxxx",
            endpoint="https://api.lybic.cn/"
        )
    ) as client:
        sandbox_id = "SBX-xxxx"
        
        # 1. Create web server script
        server_code = """#!/usr/bin/env python3
from http.server import HTTPServer, BaseHTTPRequestHandler
import json

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-Type', 'application/json')
        self.end_headers()
        response = {'status': 'ok', 'message': 'Hello from Lybic!'}
        self.wfile.write(json.dumps(response).encode())
    
    def log_message(self, format, *args):
        print(f"{self.address_string()} - {format % args}")

if __name__ == '__main__':
    server = HTTPServer(('0.0.0.0', 8080), Handler)
    print("Server started at http://0.0.0.0:8080")
    server.serve_forever()
"""
        
        stdin_data = base64.b64encode(server_code.encode()).decode()
        
        result = await client.sandbox.execute_process(
            sandbox_id,
            executable="bash",
            args=["-c", "mkdir -p /home/agent/server && cat > /home/agent/server/app.py && chmod +x /home/agent/server/app.py"],
            stdinBase64=stdin_data
        )
        
        if result.exitCode != 0:
            print("Failed to create server script")
            return
        
        print("✓ Server script created successfully")
        
        # 2. Start server with systemd
        result = await client.sandbox.execute_process(
            sandbox_id,
            executable="systemd-run",
            args=[
                "--user",
                "--unit=web-server",
                "--description=Python Web Server",
                "python3",
                "/home/agent/server/app.py"
            ]
        )
        
        output = base64.b64decode(result.stdoutBase64 or '').decode()
        print(f"✓ Web server started: {output}")
        
        # 3. Wait a few seconds for server to start
        await asyncio.sleep(3)
        
        # 4. Check server status
        result = await client.sandbox.execute_process(
            sandbox_id,
            executable="systemctl",
            args=["--user", "is-active", "web-server"]
        )
        
        status = base64.b64decode(result.stdoutBase64 or '').decode().strip()
        print(f"✓ Server status: {status}")
        
        # 5. View logs
        result = await client.sandbox.execute_process(
            sandbox_id,
            executable="journalctl",
            args=["--user", "--unit=web-server", "-n", "10", "--no-pager"]
        )
        
        logs = base64.b64decode(result.stdoutBase64 or '').decode()
        print("✓ Server logs:")
        print(logs)
        
        # 6. Test server
        result = await client.sandbox.execute_process(
            sandbox_id,
            executable="curl",
            args=["http://localhost:8080"]
        )
        
        response = base64.b64decode(result.stdoutBase64 or '').decode()
        print(f"✓ Server response: {response}")

if __name__ == "__main__":
    asyncio.run(run_web_server())
import { LybicClient } from '@lybic/core'

const lybic = new LybicClient({
  baseUrl: 'https://api.lybic.cn',
  orgId: 'ORG-xxxx',
  apiKey: 'lysk-your-api-key-here',
})

async function runWebServer() {
  const sandboxId = 'SBX-xxxx'
  
  // 1. Create web server script
  const serverCode = `#!/usr/bin/env python3
from http.server import HTTPServer, BaseHTTPRequestHandler
import json

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-Type', 'application/json')
        self.end_headers()
        response = {'status': 'ok', 'message': 'Hello from Lybic!'}
        self.wfile.write(json.dumps(response).encode())
    
    def log_message(self, format, *args):
        print(f"{self.address_string()} - {format % args}")

if __name__ == '__main__':
    server = HTTPServer(('0.0.0.0', 8080), Handler)
    print("Server started at http://0.0.0.0:8080")
    server.serve_forever()
`
  
  const stdinBase64 = btoa(serverCode)
  
  let result = await lybic.execSandboxProcess(sandboxId, {
    executable: 'bash',
    args: ['-c', 'mkdir -p /home/agent/server && cat > /home/agent/server/app.py && chmod +x /home/agent/server/app.py'],
    stdinBase64: stdinBase64,
  })
  
  if (result.data?.exitCode !== 0) {
    console.log('Failed to create server script')
    return
  }
  
  console.log('✓ Server script created successfully')
  
  // 2. Start server with systemd
  result = await lybic.execSandboxProcess(sandboxId, {
    executable: 'systemd-run',
    args: [
      '--user',
      '--unit=web-server',
      '--description=Python Web Server',
      'python3',
      '/home/agent/server/app.py',
    ],
  })
  
  const output = atob(result.data?.stdoutBase64 || '')
  console.log('✓ Web server started:', output)
  
  // 3. Wait a few seconds for server to start
  await new Promise(resolve => setTimeout(resolve, 3000))
  
  // 4. Check server status
  result = await lybic.execSandboxProcess(sandboxId, {
    executable: 'systemctl',
    args: ['--user', 'is-active', 'web-server'],
  })
  
  const status = atob(result.data?.stdoutBase64 || '').trim()
  console.log('✓ Server status:', status)
  
  // 5. View logs
  result = await lybic.execSandboxProcess(sandboxId, {
    executable: 'journalctl',
    args: ['--user', '--unit=web-server', '-n', '10', '--no-pager'],
  })
  
  const logs = atob(result.data?.stdoutBase64 || '')
  console.log('✓ Server logs:')
  console.log(logs)
  
  // 6. Test server
  result = await lybic.execSandboxProcess(sandboxId, {
    executable: 'curl',
    args: ['http://localhost:8080'],
  })
  
  const response = atob(result.data?.stdoutBase64 || '')
  console.log('✓ Server response:', response)
}

runWebServer()

Advanced Usage

Set Resource Limits

You can set CPU and memory limits for tasks:

systemd-run --user \
  --unit=resource-limited-task \
  --property=CPUQuota=50% \
  --property=MemoryMax=512M \
  python3 /home/agent/scripts/train.py

Set Environment Variables

systemd-run --user \
  --unit=task-with-env \
  --setenv=API_KEY=your-key \
  --setenv=DEBUG=true \
  python3 /home/agent/scripts/app.py

Set Working Directory

systemd-run --user \
  --unit=task-with-workdir \
  --working-directory=/home/agent/project \
  python3 train.py

Stop Task

To stop a running task:

systemctl --user stop my-training-task

List All User Tasks

systemctl --user list-units --type=service

Best Practices

  1. Use meaningful unit names: Use descriptive --unit names for easier management
  2. Add task descriptions: Use the --description parameter to add task documentation
  3. Check logs regularly: Use journalctl to monitor task output and catch issues early
  4. Set resource limits: Set reasonable resource limits for long-running tasks to avoid affecting other tasks
  5. Add error handling: Include error handling and logging in your scripts
  6. Clean up tasks: Stop unnecessary tasks when complete to free resources

FAQ

Q: How do I know when a task is complete?

Regularly use systemctl --user is-active to check task status. When it returns inactive, the task has completed.

Q: How can I see the exit code of a task?

Use systemctl --user show my-training-task to view detailed information, including the ExecMainStatus field.

Q: Will tasks continue running after the sandbox restarts?

Temporary units created with systemd-run will not automatically restart after a sandbox restart. For persistence, you need to create permanent systemd service files.

Q: Can I run multiple tasks simultaneously?

Yes, simply use different --unit names for each task.

Q: How can I get real-time logs?

Due to API timeout limits, using journalctl -f is not recommended. Instead, call the API periodically to fetch the latest logs.


On this page