BYOC smoke-test: CPU gateway and orchestrator (off-chain to on-chain)
Run a complete Livepeer gateway and orchestrator on one machine using a CPU Docker container as a BYOC pipeline, test end-to-end with off-chain payments via a remote signer, then graduate to a production on-chain setup.
This tutorial walks through a complete end-to-end test of the Livepeer gateway + orchestrator pipeline using BYOC (Bring Your Own Container) with a CPU-only Docker container. No GPU is required.By the end you will have:
A go-livepeer orchestrator running locally accepting BYOC jobs
A go-livepeer gateway running in off-chain mode pointed at a community remote signer
A simple CPU Docker container registered as a BYOC pipeline
A verified end-to-end job sent from your gateway through your orchestrator and back
A clear path to taking this setup to production on-chain
This tutorial uses the off-chain gateway mode (remote signer) for simplicity. Off-chain mode was introduced in Q4 2025 via PRs #3791 and #3822. It is the recommended starting point for all new AI and BYOC gateway deployments.
BYOC vs LV2V: BYOC uses a different job flow from LV2V (live-video-to-video). BYOC does not use gRPC to fetch OrchestratorInfo — discovery is done by HTTP capability query. The gateway’s start-stream request can explicitly set an allow-list or block-list of orchestrators. This makes BYOC well-suited to browser and CPU-only deployments where gRPC dependencies are undesirable.
The gateway handles no Ethereum operations itself. All payment signing is delegated to the community remote signer. The orchestrator is registered locally only — no on-chain registration needed for this smoke test.
We will use a minimal Python image that echoes video frames back — a CPU-only passthrough pipeline useful for testing the full job flow without any GPU or model inference.
1
Create the pipeline directory
Copy
Ask AI
mkdir byoc-cpu-testcd byoc-cpu-test
2
Write the pipeline code
Create pipeline.py:
Copy
Ask AI
import asyncioimport loggingfrom runner.live.pipelines import Pipeline, BaseParamsfrom runner.live.trickle import VideoFrame, VideoOutputclass PassthroughParams(BaseParams): """No custom parameters needed for passthrough.""" passclass PassthroughPipeline(Pipeline): """ CPU-only passthrough pipeline for BYOC smoke testing. Echoes every incoming video frame directly to the output queue. No model loading, no GPU required. """ def __init__(self): self.frame_queue: asyncio.Queue[VideoOutput] = asyncio.Queue() self._running = True async def initialize(self, **params): logging.info("PassthroughPipeline: initialized (CPU, no model loading)") async def put_video_frame(self, frame: VideoFrame, request_id: str): if self._running: # Pass the frame through unchanged await self.frame_queue.put(VideoOutput(frame, request_id)) async def get_processed_video_frame(self) -> VideoOutput: return await self.frame_queue.get() async def update_params(self, **params): logging.info(f"PassthroughPipeline: params update received: {params}") async def stop(self): self._running = False logging.info("PassthroughPipeline: stopped") @classmethod def prepare_models(cls): logging.info("PassthroughPipeline: no models to prepare (CPU passthrough)")
3
Write the entrypoint
Create main.py:
Copy
Ask AI
import osfrom runner.app import start_appfrom runner.live.pipelines import PipelineSpecpipeline_spec = PipelineSpec( name="passthrough-cpu", # This is your model_id in go-livepeer pipeline_cls="pipeline:PassthroughPipeline", params_cls="pipeline:PassthroughParams", initial_params={},)if __name__ == "__main__": start_app(pipeline=pipeline_spec)
4
Write the Dockerfile
Create Dockerfile:
Copy
Ask AI
# Pin to a specific ai-runner live-base versionARG BASE_IMAGE=livepeer/ai-runner:live-baseFROM ${BASE_IMAGE}WORKDIR /app# Copy pipeline filesCOPY pipeline.py ./pipeline.pyCOPY main.py ./main.py# No additional dependencies needed for CPU passthroughENV HF_HUB_OFFLINE=1ENTRYPOINT ["python", "main.py"]
-cliAddr — separate CLI port from the orchestrator (must differ)
-httpAddr — AI API port that your applications will call
-orchAddr — point directly at your local orchestrator
-remoteSignerAddr — the community-hosted remote signer (provides free ETH for testing)
-network offchain — no Arbitrum RPC required
-datadir — separate data directory
Successful startup logs include:
Copy
Ask AI
Gateway started on :8935Connected to remote signer at https://signer.eliteencoder.netRegistered orchestrator: localhost:8935
The remote signer at signer.eliteencoder.net is a community-hosted service maintained by John (Elite Encoder). It provides free ETH for testing off-chain gateway setups. Confirm availability in #local-gateways on Discord if you encounter connection errors.
If this fails, post in #local-gateways on Discord — the signer may be down or the URL may have changed. As a fallback, you can run your own remote signer from the go-livepeer source using the -remoteSigner flag with your own Ethereum key.
Orchestrator not registering the BYOC capability
Check that your Docker container is running and reachable before starting the orchestrator:
If the container is not reachable, the orchestrator will start but will not advertise the BYOC capability to the gateway.
Port conflict errors
If either port 7935, 7936, or 8935 is already in use:
Copy
Ask AI
lsof -i :8935lsof -i :7935
Adjust the -serviceAddr, -cliAddr, and -httpAddr flags accordingly. Make sure -orchAddr on the gateway matches the orchestrator’s -serviceAddr.
Gateway cannot find the orchestrator
Check that the orchestrator’s -serviceAddr is reachable from the gateway process:
Copy
Ask AI
curl http://localhost:8935/getOrchestrators
If the orchestrator is on a different interface, update -orchAddr to use the correct IP. For a same-machine setup, localhost or 127.0.0.1 should always work.
BYOC container exits immediately
Check Docker logs:
Copy
Ask AI
docker logs byoc-cpu-passthrough
Common causes: missing ai-runner base image (pull it first with docker pull livepeer/ai-runner:live-base), or a Python import error in your pipeline code.
Once your off-chain smoke test passes, follow this path to move to a production on-chain deployment.
1
Acquire ETH on Arbitrum One
On-chain gateways require a PM deposit (approximately 0.065 ETH) and a PM reserve (approximately 0.03 ETH) on Arbitrum One.Options for acquiring Arbitrum ETH:
The gateway will now discover orchestrators from the on-chain registry and sign PM tickets directly using your wallet’s signing key.
Once on-chain, you no longer need the community remote signer. Your gateway holds the PM signing key and handles all Ethereum operations directly. This is the standard mode for video gateways and is also supported for AI gateways that prefer full on-chain custody.