Deploy the complete Jeju stack on Oracle Cloud ARM64 (Ampere) instances.
This deployment is designed for Oracle Cloud's Always Free tier using ARM64 processors.
| Service | Port | Description |
|---|---|---|
| L1 Geth | 8545 | Ethereum L1 (--dev mode) |
| op-geth | 9545 | L2 Execution Layer |
| op-node | 7545 | L2 Consensus Layer |
| op-batcher | 6545 | Batch Submitter |
| Service | Port | Description |
|---|---|---|
| PostgreSQL | 5432 | Database for Indexer/Crucible |
| SQLit | 4661 | Distributed state layer for DWS |
| Service | Port | Description |
|---|---|---|
| DWS | 4030 | Decentralized Web Services coordinator |
| Indexer | 4350-4352 | Blockchain indexer (GraphQL, REST, A2A) |
| Crucible | 4020-4022 | Agent orchestration platform |
| Factory | 4008 | Developer coordination hub |
| Gateway | 4001 | Bridge, staking, token registry |
| Wallet | 4015 | Crypto wallet |
| Node | 4070 | Infrastructure provider |
# SSH into Oracle instance
ssh ubuntu@<your-ip>
# Clone repo
git clone https://github.com/jejunetwork/jeju.git
cd jeju/packages/deployment/oracle-cloud-arm64
# Start L2 blockchain
cd docker
docker compose up -d l1-geth
sleep 10
../scripts/init-genesis.sh phase1
docker compose up -d op-geth
sleep 15
../scripts/init-genesis.sh phase2
docker compose up -d op-node op-batcher
# Start infrastructure
docker compose up -d postgres sqlit
# Start apps
docker compose up -d dws indexer
docker compose up -d crucible factory gateway wallet nodeAll Dockerfiles in this deployment are ARM64-native:
- Use
oven/bun:1.3(multi-arch) - Use
postgres:15(multi-arch) - Use
nginx:alpine(multi-arch) - Replaced x86_64 binaries with ARM64 equivalents (e.g., workerd)
Images are built on-demand the first time you run docker compose up:
# Build all app images
docker compose build
# Build specific service
docker compose build dws
# Rebuild with no cache
docker compose build --no-cache indexeroracle-cloud-arm64/
├── docker/
│ ├── docker-compose.yaml # All services
│ ├── config/ # L2 config files
│ │ ├── genesis.json
│ │ └── rollup.json
│ ├── jwt/
│ │ └── jwt.hex # JWT secret
│ └── Dockerfiles/ # ARM64-specific Dockerfiles
│ ├── Dockerfile.dws
│ ├── Dockerfile.indexer
│ ├── Dockerfile.crucible
│ ├── Dockerfile.factory
│ ├── Dockerfile.gateway
│ ├── Dockerfile.wallet
│ ├── Dockerfile.node
│ └── Dockerfile.sqlit
├── scripts/
│ └── init-genesis.sh # Genesis initialization
└── README.md
NETWORK=localnet
RPC_URL=http://op-geth:9545
SQLIT_URL=http://sqlit:4661DB_HOST=postgres
DB_PORT=5432
DB_NAME=indexer
DB_USER=postgres
DB_PASS=postgres
RPC_ENDPOINT=http://op-geth:9545
MODE=processor # processor, api, graphql, or fullDWS_URL=http://dws:4030
DATABASE_URL=postgres://postgres:postgres@postgres:5432/crucible
RPC_URL=http://op-geth:9545PORT=4008
RPC_URL=http://op-geth:9545
DWS_URL=http://dws:4030VITE_RPC_URL=http://localhost:9545
VITE_NETWORK=localnetPORT=4015
VITE_RPC_URL=http://localhost:9545
VITE_NETWORK=localnetJEJU_NETWORK=localnet
PORT=4070
RPC_URL=http://op-geth:9545
DWS_URL=http://dws:4030l1-geth
└── op-geth
└── op-node
└── op-batcher
└── dws (requires sqlit)
└── crucible
└── node
└── indexer (requires postgres)
└── factory
└── gateway
└── wallet
Check logs:
docker compose logs <service-name>
docker compose logs -f <service-name> # Follow logsARM64 builds may fail if Dockerfiles reference x86_64-only packages. Check for platform-specific binaries and replace with ARM64 equivalents.
DWS requires SQLit. Ensure SQLit is running:
docker compose up -d sqlit
docker compose logs sqlitIf you see "Cannot find module @jejunetwork/..." errors, the workspace symlinks
may be incorrect. Rebuild the image with --no-cache:
docker compose build --no-cache <service>Re-run init-genesis.sh phase1 after L1 restarts:
../scripts/init-genesis.sh phase1Check op-node health and logs:
docker compose logs op-node
curl localhost:7545/healthzIf persistent issues occur, do a clean restart:
docker compose down
docker volume rm docker_l1-data docker_l2-data
docker compose up -d l1-geth
sleep 10
../scripts/init-genesis.sh phase1
docker compose up -d op-geth
sleep 15
../scripts/init-genesis.sh phase2
docker compose up -d op-node op-batcherThe indexer uses Subsquid with TypeORM which requires decorator metadata for entity definitions. This requires special handling:
-
Bun Runtime Issue: Bun doesn't handle circular dependencies in TypeORM decorators correctly, causing
ReferenceError: Cannot access 'X' before initialization -
tsx Runtime Issue: Using tsx (TypeScript execution for Node.js) doesn't emit decorator metadata, causing
ColumnTypeUndefinedError: Column type for X#id is not defined and cannot be guessed
Solution: The indexer Dockerfile compiles TypeScript to JavaScript in the builder
stage using tsc (which emits decorator metadata with emitDecoratorMetadata: true),
then runs the compiled JavaScript in the production stage:
# In builder stage - compile TypeScript
RUN bun x tsc --outDir lib --declaration false --sourceMap false
# In production stage - run compiled JavaScript
CMD ["node", "lib/api/main.js"]Different apps have different entry points. Check the app's package.json or
scripts/ directory for the correct entry point:
| App | Entry Point | Notes |
|---|---|---|
| DWS | scripts/serve.ts |
Bun runtime |
| Indexer | lib/api/main.js |
Compiled JS, Node.js runtime |
| Crucible | scripts/start.ts |
Bun runtime |
| Factory | scripts/serve.ts |
Bun runtime |
| Node | scripts/serve.ts |
Bun runtime |
| Gateway | nginx static | Vite SPA |
| Wallet | nginx static | Vite SPA |
Some services may show "unhealthy" in docker compose ps even when they're working.
This is because the healthcheck uses curl or wget which aren't available in
slim images. The services still work correctly - verify with:
curl http://localhost:4030/health # DWS
curl http://localhost:4661/health # SQLit
curl http://localhost:4008/health # FactoryThe workspace packages (@jejunetwork/*) are linked via symlinks in node_modules. The symlink paths differ between:
- Builder stage:
../../packages/X(from /app) - Runtime stage:
../../../../packages/X(from /app/apps/indexer/node_modules/@jejunetwork/X)
Special package name mappings:
packages/bridge→@jejunetwork/zksolbridgeapps/dws→@jejunetwork/dws
Gateway and Wallet are Vite-built SPAs. They require:
- Vite build to create static assets
- nginx to serve the built files
- Environment variables at build time (not runtime)
To update environment variables, rebuild the images.
Factory requires SQLit for distributed state. Ensure these environment variables are set in docker-compose.yaml:
factory:
environment:
- SQLIT_ENDPOINT=http://sqlit:4661
- SQLIT_DATABASE_ID=factory
depends_on:
sqlit:
condition: service_startedThe rollup.json file must not contain _comment fields (op-node can't parse them).
It also requires actual genesis hashes from the running L1/L2 chains:
{
"genesis": {
"l1": {
"hash": "<actual-l1-genesis-hash>",
"number": 0
},
"l2": {
"hash": "<actual-l2-genesis-hash>",
"number": 0
},
"l2_time": <actual-l2-genesis-timestamp>
}
}Get these values after starting L1 and L2:
# L1 genesis hash
curl -s -X POST http://localhost:8545 -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x0",false],"id":1}' \
| jq -r '.result.hash'
# L2 genesis hash and timestamp
curl -s -X POST http://localhost:9545 -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x0",false],"id":1}' \
| jq -r '.result.hash, .result.timestamp'After deployment, services are accessible at:
| Service | URL |
|---|---|
| L2 RPC | http://:9545 |
| DWS | http://:4030 |
| Indexer GraphQL | http://:4350/graphql |
| Indexer REST | http://:4352 |
| Crucible | http://:4020 |
| Factory | http://:4008 |
| Gateway | http://:4001 |
| Wallet | http://:4015 |
| Node | http://:4070 |
A working Jeju L2 image is available for quick deployment:
Public Download (QCOW2):
https://objectstorage.us-sanjose-1.oraclecloud.com/n/axhupkjmbqfj/b/jeju-images/o/jeju-l2-arm64.qcow2
Oracle Cloud Image OCID (us-sanjose-1 region):
ocid1.image.oc1.us-sanjose-1.aaaaaaaa4mp76zw6gouybvxuqkuimznt4jfnylwn3xdaihshjflj5wcbilha
| Property | Value |
|---|---|
| Name | jeju-l2-working-20260126 |
| Format | QCOW2 (ARM64) |
| Size | 1.83 GB |
| Shape | VM.Standard.A1.Flex |
| OS | Ubuntu 24.04 |