Get started
Five-minute quick start Beta
Start the DEV Docker stack with the proxy, editing server, o3o-gate and DocBuilder, check each service, open the embedded editor and convert a .docx file to PDF.
This page describes a beta feature: it works but may still change.
On this page
This page takes you from nothing to O3O Office Online running on your machine. Five minutes assumes the images are already on the machine; the first run also depends on image download speed (not measured).
Prerequisites#
- Docker Engine or Docker Desktop (Linux engine) with Docker Compose v2.
- Port
8080free; port8081as well if you try Nextcloud. curlto check the services, and any.docxfile to test conversion.- The
online/folder of the O3O Office Online source. If you do not have it, contact office@o3o.vn. - Minimum hardware: not measured. The v1 specification was verified on Docker Desktop with the Linux engine.
Step 1. Create the .env file#
Every command on this page runs in the online/ folder. Create .env with the secrets script in DEV mode: it copies .env.example to .env, replaces every CHANGE_ME value with a random 32-character string and generates O3O_GATE_INSTANCE_ID. This step is required because the nextcloud profile will not start while CHANGE_ME remains. The script uses bash syntax, so run it with bash, not sh.
cd online
bash scripts/gen-secrets.sh --devcd online
powershell -ExecutionPolicy Bypass -File scripts\gen-secrets.ps1 -DevStep 2. Build the images and start#
The first time, build the two O3O images, o3o/gate:dev and o3o/docbuilder:dev (the build context is always the online/ folder), then start the stack:
docker build -f gate/Dockerfile -t o3o/gate:dev .
docker build -f docbuilder/Dockerfile -t o3o/docbuilder:dev .
docker compose --env-file .env -f docker/compose.dev.yml up -d
docker compose --env-file .env -f docker/compose.dev.yml psFour services come up: o3o-proxy, o3o-online, o3o-gate and o3o-docbuilder. Only the proxy publishes a port; the other services talk inside the o3o-net network. The editing server needs a little longer after its container starts; use step 3 to see when it is ready.
Step 3. Check each service#
curl -s http://localhost:8080/o3o/healthz
curl -s http://localhost:8080/o3o/status
curl -s http://localhost:8080/v1/status
curl -s http://localhost:8080/hosting/discovery | head -c 400| Request | Expected result |
|---|---|
/o3o/healthz | {"ok": true}: the o3o-gate process is alive. |
/o3o/status | edition is community, connections.limit is 50, upstream.coolwsd is ok once the editing server is ready, dev_image is true. |
/v1/status | service is o3o-docbuilder; the core field shows the LibreOffice version running in the container; workers.total is at least 1. |
/hosting/discovery | XML with urlsrc entries like http://localhost:8080/browser/<hash>/cool.html?. If the port or host name is wrong, fix O3O_ONLINE_SERVER_NAME. |
{
"service": "o3o-gate",
"version": "1.0.0",
"api": "v1",
"edition": "community",
"dev_mode": true,
"dev_image": true,
"license": {
"state": "none",
"message": "Không có token bản quyền. Đang chạy bản cộng đồng."
},
"connections": {
"limit": 50,
"current": 0,
"pending": 0,
"peak_5m": 0,
"readonly": 0,
"limit_reached": false,
"source": "adminws",
"enforcing": true
},
"upstream": {
"coolwsd": "ok"
},
"embed": {
"enabled": true,
"jwt_required": false,
"callback_signing": true
}
}Step 4. Open the embedded editor#
With O3O_DEV_MODE=1, open http://localhost:8080/o3o/demo: the test page opens the sample sample.docx for editing, prints every event to a log panel and has buttons that call save(), setReadOnly(), close() and getInfo().
Embedding in your own page takes one container element and one script tag. Save the snippet below as an HTML file and open it through any web server on localhost:
<!doctype html>
<html lang="en">
<body style="margin:0">
<div id="editor" style="height:100vh"></div>
<script src="http://localhost:8080/o3o/api.js"></script>
<script>
new O3O.Editor("editor", {
document: {
url: "http://localhost:8080/o3o/demo/sample.docx",
title: "Sample document.docx",
fileType: "docx",
key: "quickstart-1"
},
editor: { mode: "edit", lang: "en", user: { id: "dev-01", name: "Developer" } },
events: {
onDocumentLoaded: function () { console.log("Document loaded"); },
onError: function (e) { console.error(e.code, e.message); }
}
});
</script>
</body>
</html>Step 5. Convert a file to PDF#
DocBuilder takes the API key in the Authorization: Bearer header. Put the first key of O3O_DOCBUILDER_API_KEYS from .env into the O3O_DEMO_KEY environment variable:
# First key of O3O_DOCBUILDER_API_KEYS in .env (drops the "name:" prefix if present)
export O3O_DEMO_KEY="$(grep '^O3O_DOCBUILDER_API_KEYS=' .env | cut -d= -f2- | cut -d, -f1 | sed 's/^[^:]*://')"
# Print the key length to confirm it was read (valid keys are 24 to 128 characters)
echo "${#O3O_DEMO_KEY} characters"# First key of O3O_DOCBUILDER_API_KEYS in .env (drops the "name:" prefix if present)
$line = (Select-String -Path .env -Pattern '^O3O_DOCBUILDER_API_KEYS=').Line
$env:O3O_DEMO_KEY = (($line -split '=', 2)[1] -split ',')[0] -replace '^[^:]*:', ''
# Print the key length to confirm it was read (valid keys are 24 to 128 characters)
$env:O3O_DEMO_KEY.Length/v1/convertConverts between formats within one document family (text, spreadsheet, presentation). Send the file as multipart/form-data or send a url as JSON.
curl -s -X POST http://localhost:8080/v1/convert \
-H "Authorization: Bearer $O3O_DEMO_KEY" \
-F "file=@contract.docx" \
-F "to=pdf" \
-o contract.pdfcurl.exe -s -X POST http://localhost:8080/v1/convert `
-H "Authorization: Bearer $env:O3O_DEMO_KEY" `
-F "file=@contract.docx" `
-F "to=pdf" `
-o contract.pdf// Node.js 18 or later (fetch, FormData and Blob are built in)
import { readFile, writeFile } from "node:fs/promises";
const form = new FormData();
form.append("file", new Blob([await readFile("contract.docx")]), "contract.docx");
form.append("to", "pdf");
const res = await fetch("http://localhost:8080/v1/convert", {
method: "POST",
headers: { Authorization: "Bearer " + process.env.O3O_DEMO_KEY },
body: form
});
if (!res.ok) throw new Error((await res.json()).error.code);
await writeFile("contract.pdf", Buffer.from(await res.arrayBuffer()));import os
import requests
with open("contract.docx", "rb") as f:
r = requests.post(
"http://localhost:8080/v1/convert",
headers={"Authorization": "Bearer " + os.environ["O3O_DEMO_KEY"]},
files={"file": ("contract.docx", f)},
data={"to": "pdf"},
timeout=120,
)
r.raise_for_status()
with open("contract.pdf", "wb") as f:
f.write(r.content)<?php
$ch = curl_init("http://localhost:8080/v1/convert");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("O3O_DEMO_KEY")],
CURLOPT_POSTFIELDS => ["file" => new CURLFile("contract.docx"), "to" => "pdf"],
CURLOPT_RETURNTRANSFER => true,
]);
$pdf = curl_exec($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) !== 200) {
exit($pdf);
}
file_put_contents("contract.pdf", $pdf);using System.Net.Http.Headers;
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("O3O_DEMO_KEY"));
using var form = new MultipartFormDataContent();
form.Add(new ByteArrayContent(File.ReadAllBytes("contract.docx")), "file", "contract.docx");
form.Add(new StringContent("pdf"), "to");
var res = await http.PostAsync("http://localhost:8080/v1/convert", form);
res.EnsureSuccessStatusCode();
File.WriteAllBytes("contract.pdf", await res.Content.ReadAsByteArrayAsync());X-O3O-Job-Id and X-O3O-File-Id headers carry the job and file identifiers.detail.reason is missing, invalid or no_keys_configured; the message text below is only an example.{
"error": {
"code": "unauthorized",
"message": "Khoá API không hợp lệ.",
"detail": {
"reason": "invalid"
},
"request_id": "req_0123456789abcdef"
}
}The community edition accepts files up to 10 MB, 10 processing requests per minute and 200 per day. Full limits are on Editions and feature differences.
Step 6 (optional). Try it with Nextcloud#
The nextcloud profile adds Nextcloud 30, MariaDB 11 and Redis 7; Nextcloud is served at http://localhost:8081. Start the profile, then run the setup-nextcloud script: it waits for Nextcloud to finish installing, installs the richdocuments connector app, runs richdocuments:activate-config with both the editing server's internal address (-w http://o3o-online:9980) and the address the editing server uses to call Nextcloud back (-c http://o3o-nextcloud), reads the configuration back, then creates a test user with three sample files. The test user's name and password are printed at the end.
docker compose --env-file .env -f docker/compose.dev.yml --profile nextcloud up -d
bash scripts/setup-nextcloud.shdocker compose --env-file .env -f docker/compose.dev.yml --profile nextcloud up -d
powershell -ExecutionPolicy Bypass -File scripts\setup-nextcloud.ps1To do it by hand, wait until http://localhost:8081/status.php reports "installed":true, then run the two occ commands below. The second one must have both -w and -c:
docker compose --env-file .env -f docker/compose.dev.yml --profile nextcloud exec -u www-data o3o-nextcloud php occ app:install richdocuments
docker compose --env-file .env -f docker/compose.dev.yml --profile nextcloud exec -u www-data o3o-nextcloud php occ richdocuments:activate-config -w http://o3o-online:9980 -c http://o3o-nextcloudWhen everything works#
/o3o/statusreportsupstream.coolwsd = okandconnections.enforcing = true.- The
/o3o/demopage firesonReadythenonDocumentLoaded; typing and saving firesonSaved. - The conversion command produces a PDF that opens.
- Opening the same document for editing in two tabs raises
connections.currentby 2; opening it read-only leaves it unchanged.
Clean up#
docker compose --env-file .env -f docker/compose.dev.yml --profile nextcloud down -v