Skip to main content

Deployment

Dockerfile

FROM octostar/streamlit-apps-no-gpu:latest

WORKDIR /app

ENV PATH="/root/.local/bin:$PATH"
ENV VIRTUAL_ENV=/app/venv
ENV PATH="/app/venv/bin:$PATH"
ENV CODE_SERVER_PORT=8080

# Create a venv and install uv for fast dependency resolution
RUN python3 -m venv "$VIRTUAL_ENV" \
&& "$VIRTUAL_ENV/bin/pip" install --upgrade pip \
&& "$VIRTUAL_ENV/bin/pip" install uv

COPY . /app

# Normalize line endings (important on Windows), then build
RUN sed -i 's/\r$//' build.sh && chmod +x build.sh && bash build.sh
RUN sed -i 's/\r$//' main.sh && chmod +x main.sh

ENTRYPOINT ["/bin/bash", "/app/main.sh"]
EXPOSE $CODE_SERVER_PORT

The base image provides a Debian environment with Python and common system libraries. It does not include the Octostar Python SDK — that is downloaded at container startup by main.sh (see Runtime below).

Build Script (build.sh)

Runs once at image build time. It installs Node.js 20 (not present in the base image), installs Python backend dependencies, and builds the React frontend:

# Install Node.js 20 via NodeSource
apt-get install -y nodejs

# Install Python deps using uv (fast)
/app/venv/bin/uv pip install -r requirements.txt --no-cache-dir

# Build React frontend → outputs to frontend/dist/
cd /app/frontend
npm install --legacy-peer-deps
npm run build

--legacy-peer-deps is required because some @octostar/* packages have peer dependency declarations that conflict with the strict default resolver in npm 7+.

Runtime (main.sh)

Runs every time the container starts. Before launching the server it downloads and installs the Octostar Python client from your platform instance, ensuring the SDK version always matches the running platform:

# Download the Octostar Python client from the platform
curl -sSL -o /tmp/client.tar.gz \
"${OS_API_ENDPOINT}/api/octostar/meta/octostar-python-client.tar.gz"

/app/venv/bin/uv pip install --no-cache-dir /tmp/client.tar.gz

# Start FastAPI — serves both the API and the built frontend
exec /app/venv/bin/uvicorn backend.main:app \
--host 0.0.0.0 \
--port "${CODE_SERVER_PORT:-8080}"

FastAPI: Serving the Frontend

backend/main.py defines API routes first, then mounts the built frontend as a static site fallback. The mount only activates if frontend/dist/ exists (i.e. after a build):

from fastapi.staticfiles import StaticFiles
import os

app = FastAPI()

# API routes — all prefixed with /api
@app.get("/api/health")
def health():
return {"status": "ok"}

# Serve built React frontend (SPA fallback)
_dist = os.path.join(os.path.dirname(__file__), "..", "frontend", "dist")
if os.path.exists(_dist):
app.mount("/", StaticFiles(directory=_dist, html=True), name="frontend")

The template includes a GitHub Actions workflow at .github/workflows/docker-build-push.yml that builds and pushes a multi-architecture Docker image on every git tag.

Setup

Add two secrets to your GitHub repository (Settings → Secrets → Actions):

SecretValue
DOCKERHUB_USERNAMEYour Docker Hub username
DOCKERHUB_TOKENA Docker Hub access token (not your password)

How the workflow runs

The build uses a matrix strategy to build linux/amd64 and linux/arm64 in parallel on separate runners, then merges them into a single multi-arch manifest list:

Push tag (e.g. 0.1.0)
├── Job: docker-build (linux/amd64) → pushes digest
├── Job: docker-build (linux/arm64) → pushes digest
└── Job: merge_docker_images
└── Creates manifest list:
octostar/app.your-slug:0.1.0
octostar/app.your-slug:latest

Both arch builds run in parallel. The merge job runs after both complete and creates the final tagged image.

Releasing a new version

git add .
git commit -m "Your changes"
git tag 0.2.0
git push origin master
git push origin 0.2.0

The tag push triggers the workflow. Monitor progress in GitHub → Actions. The image will be available on Docker Hub as octostar/app.your-slug:0.2.0 once the merge job completes.

Deploying to Octostar

Once the image is pushed:

  1. Open your app folder in Octostar with the App Editor.
  2. Upload manifest.yaml to the app folder.
  3. Open manifest.yaml in the Monaco Editor — update the image field to include the version tag you just pushed:
  4. image: octostar/app.your-slug:0.2.0
  5. Save, then click Deploy App in the App Editor.
  6. Disable Developer Mode if it was enabled. The app is now live. It will appear in the platform launcher and context menus according to your manifest.yaml configuration.