Skip to content

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 8080 free; port 8081 as well if you try Nextcloud.
  • curl to check the services, and any .docx file 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.

Create .env
cd online
bash scripts/gen-secrets.sh --dev
cd online
powershell -ExecutionPolicy Bypass -File scripts\gen-secrets.ps1 -Dev

Step 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:

BashBuild and start
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 ps

Four 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#

BashFour checks
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
RequestExpected result
/o3o/healthz{"ok": true}: the o3o-gate process is alive.
/o3o/statusedition is community, connections.limit is 50, upstream.coolwsd is ok once the editing server is ready, dev_image is true.
/v1/statusservice is o3o-docbuilder; the core field shows the LibreOffice version running in the container; workers.total is at least 1.
/hosting/discoveryXML with urlsrc entries like http://localhost:8080/browser/<hash>/cool.html?. If the port or host name is wrong, fix O3O_ONLINE_SERVER_NAME.
200Shortened example of GET /o3o/status on a DEV machine (the server returns messages in Vietnamese).
{
  "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:

HTMLMinimal embedding page
<!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:

Load the sample key
# 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
POST/v1/convert

Converts between formats within one document family (text, spreadsheet, presentation). Send the file as multipart/form-data or send a url as JSON.

Auth: BearerCommunityEnterprise
Convert .docx to PDF
curl -s -X POST http://localhost:8080/v1/convert \
  -H "Authorization: Bearer $O3O_DEMO_KEY" \
  -F "file=@contract.docx" \
  -F "to=pdf" \
  -o contract.pdf
curl.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());
200Synchronous mode returns the PDF itself. The X-O3O-Job-Id and X-O3O-File-Id headers carry the job and file identifiers.
401Missing or wrong key. 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.

Start the nextcloud profile and connect it
docker compose --env-file .env -f docker/compose.dev.yml --profile nextcloud up -d
bash scripts/setup-nextcloud.sh
docker compose --env-file .env -f docker/compose.dev.yml --profile nextcloud up -d
powershell -ExecutionPolicy Bypass -File scripts\setup-nextcloud.ps1

To 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:

BashBy hand with occ
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-nextcloud

When everything works#

  • /o3o/status reports upstream.coolwsd = ok and connections.enforcing = true.
  • The /o3o/demo page fires onReady then onDocumentLoaded; typing and saving fires onSaved.
  • The conversion command produces a PDF that opens.
  • Opening the same document for editing in two tabs raises connections.current by 2; opening it read-only leaves it unchanged.

Clean up#

BashStop and remove
docker compose --env-file .env -f docker/compose.dev.yml --profile nextcloud down -v

Next steps#