Skip to main content

Run Otoroshi

Now you are ready to run Otoroshi. You can run the following command with some tweaks depending on the way you want to configure Otoroshi. If you want to pass a custom configuration file, use the -Dconfig.file=/path/to/file.conf flag in the following commands.

warning

-Dconfig.file expects an absolute path. With a relative one (-Dconfig.file=otoroshi.conf), Otoroshi fails to boot with a misleading No application loader is configured error.

From .zip file​

cd otoroshi-vx.x.x
./bin/otoroshi

From .jar file​

Otoroshi requires Java 17 or later. The jar is compiled for Java 17, and the official docker images ship JDK 17, 21 and 25.

Because the gateway reaches into internal JDK APIs for TLS and certificate handling, the following flags are required on every supported JDK:

java \
--add-opens=java.base/javax.net.ssl=ALL-UNNAMED \
--add-opens java.base/jdk.internal.misc=ALL-UNNAMED \
--add-opens=java.base/sun.net.www.protocol.file=ALL-UNNAMED \
--add-exports=java.base/sun.security.x509=ALL-UNNAMED \
--add-opens=java.base/sun.security.ssl=ALL-UNNAMED \
--enable-native-access=ALL-UNNAMED \
-Dlog4j2.formatMsgNoLookups=true \
-jar otoroshi.jar

--enable-native-access=ALL-UNNAMED lets the native libraries some features load (jq, quic, brotli, ...) call the restricted methods of the JDK: JDK 24 and later print a warning without it, and a future JDK blocks them.

From docker​

docker run -p "8080:8080" maif/otoroshi

You can also pass useful args like :

docker run -p "8080:8080" maif/otoroshi -Dconfig.file=/usr/app/otoroshi/conf/otoroshi.conf -Dlogger.file=/usr/app/otoroshi/conf/otoroshi.xml

Everything you pass after the image name is forwarded as-is to the JVM, so these paths are the ones inside the container. Mount your file first (see the volume examples below), then point -Dconfig.file at its absolute path in the container.

If you want to provide your own config file, you can read the documentation about config files.

You can also provide some ENV variable using the --env flag to customize your Otoroshi instance.

The list of possible env variables is available here.

You can use a volume to provide configuration like :

docker run -p "8080:8080" -v "$(pwd):/usr/app/otoroshi/conf" maif/otoroshi

You can also use a volume if you choose to use filedb datastore like :

docker run -p "8080:8080" -v "$(pwd)/filedb:/usr/app/otoroshi/filedb" maif/otoroshi -Dotoroshi.storage=file

You can also use a volume if you choose to use exports files :

docker run -p "8080:8080" -v "$(pwd):/usr/app/otoroshi/imports" maif/otoroshi -Dotoroshi.importFrom=/usr/app/otoroshi/imports/export.json

Memory and JVM options​

The image sizes the JVM from the limits of the container, so most of the time there is nothing to set:

  • heap: with a memory limit (docker run --memory=2g, resources.limits.memory on Kubernetes), the heap gets 60% of it. Otoroshi also uses 300 to 550 MB out of the heap, for its classes, the compiled code, the threads, the garbage collector and the buffers, so under 1280 MB the heap only gets what is left once 512 MB are set aside: half of a 1 GB container. Without a memory limit, the JVM keeps its own default, 25% of the memory of the machine ;
  • garbage collector: G1, whatever the size of the container. By itself, the JVM picks the serial collector, which stops every thread at each collection, under 2 CPUs or 1792 MB ;
  • CPUs: the JVM sizes its thread pools and its garbage collector for the CPU limit of the container (--cpus, resources.limits.cpu). Without one it sizes them for every CPU of the machine: on a large host, give the container a CPU limit or set -XX:ActiveProcessorCount=<n> ;
  • native memory: MALLOC_ARENA_MAX is set to 2, which keeps 100 to 150 MB of native memory from being held for nothing ;
  • out of memory: the JVM exits (-XX:+ExitOnOutOfMemoryError), for the container to be restarted. Otoroshi does not recover from an exhausted heap: its HTTP server stops while the process stays up, which leaves a container that looks alive and serves nothing.

Use the JAVA_OPTS environment variable to change any of it. Its options come after the ones of the image on the command line, so they win:

# a larger share of the memory for the heap
docker run -p "8080:8080" --memory=4g -e JAVA_OPTS="-XX:MaxRAMPercentage=70.0" maif/otoroshi
# a fixed heap
docker run -p "8080:8080" --memory=4g -e JAVA_OPTS="-Xms2g -Xmx2g" maif/otoroshi
# another garbage collector
docker run -p "8080:8080" --memory=8g -e JAVA_OPTS="-XX:+UseZGC" maif/otoroshi
# keep the JVM up when it runs out of memory
docker run -p "8080:8080" --memory=4g -e JAVA_OPTS="-XX:-ExitOnOutOfMemoryError" maif/otoroshi

The options the JVM is started with are printed when the container starts (JAVA_OPTS: ...).

warning

An option the JVM does not know stops the container from starting, with the name of the option in its logs. Before 18.0.0 the image told the JVM to ignore such options, so a mistyped -XX:MaxRamPercentage=70 was silently dropped: check your JAVA_OPTS when upgrading.

tip

Give a production instance at least 2 CPUs and 2 GB of memory: Otoroshi starts from 768 MB, not under. An instance reaches its full speed once the JVM has compiled the code that handles the requests: from half a minute of traffic on a large instance to several minutes with a single CPU, where the compiler shares it with the requests.

Run examples​

$ java \
-Xms2G \
-Xmx8G \
-Dhttp.port=8080 \
-Dotoroshi.importFrom=/home/user/otoroshi.json \
-Dconfig.file=/home/user/otoroshi.conf \
-jar ./otoroshi.jar

[warn] otoroshi-in-memory-datastores - Now using InMemory DataStores
[warn] otoroshi-env - The main datastore seems to be empty, registering some basic services
[warn] otoroshi-env - Importing from: /home/user/otoroshi.json
[info] play.api.Play - Application started (Prod)
[info] p.c.s.AkkaHttpServer - Listening for HTTP on /0:0:0:0:0:0:0:0:8080

If you choose to start Otoroshi without importing existing data, Otoroshi will create a new admin user and print the login details in the log. When you will log into the admin dashboard, Otoroshi will ask you to create another account to avoid security issues.

$ java \
-Xms2G \
-Xmx8G \
-Dhttp.port=8080 \
-jar otoroshi.jar

[warn] otoroshi-in-memory-datastores - Now using InMemory DataStores
[warn] otoroshi-env - The main datastore seems to be empty, registering some basic services
[warn] otoroshi-env - You can log into the Otoroshi admin console with the following credentials: admin@otoroshi.io / HHUsiF2UC3OPdmg0lGngEv3RrbIwWV5W
[info] play.api.Play - Application started (Prod)
[info] p.c.s.AkkaHttpServer - Listening for HTTP on /0:0:0:0:0:0:0:0:8080

Initial admin account​

The first time Otoroshi starts on an empty datastore (and only on a Leader or standalone instance, never on a cluster worker), it creates a default admin account. You can set its credentials explicitly, otherwise the login defaults to admin@otoroshi.io and a random 32 characters password is generated.

ConfigurationEnvironment variableDefaultDescription
otoroshi.adminLoginOTOROSHI_INITIAL_ADMIN_LOGINadmin@otoroshi.iothe initial admin login
otoroshi.adminPasswordOTOROSHI_INITIAL_ADMIN_PASSWORDrandomly generatedthe initial admin password
otoroshi.hideInitialAdminPasswordOTOROSHI_HIDE_INITIAL_ADMIN_PASSWORDfalsedo not print the initial admin password in the logs, even when it has been randomly generated
otoroshi.writeInitialAdminPasswordToFileOTOROSHI_WRITE_INITIAL_ADMIN_PASSWORD_TO_FILEfalsewrite the randomly generated initial admin password to a file instead of printing it in the logs
otoroshi.initialAdminPasswordFileOTOROSHI_INITIAL_ADMIN_PASSWORD_FILE/etc/otoroshi/initial_admin_passwordpath of the file where the randomly generated initial admin password is written

When the password is provided through otoroshi.adminPassword (or its environment variable), it is never printed in the logs nor written to a file: you already know it.

When the password is randomly generated, Otoroshi makes it available to you, depending on the flags above:

  • if otoroshi.writeInitialAdminPasswordToFile is true, it is written to otoroshi.initialAdminPasswordFile (created with owner read/write only permissions, i.e. 0600, à la GitLab /etc/gitlab/initial_root_password) and never printed in the logs — only the path is logged. This takes precedence over hideInitialAdminPassword ;
  • otherwise, if otoroshi.hideInitialAdminPassword is true, the password is not printed in the logs at all (and you will need an explicit adminPassword to log in) ;
  • otherwise (the default), it is printed once in the logs (You can log into the Otoroshi admin console with the following credentials: ...).
$ java \
-Dotoroshi.writeInitialAdminPasswordToFile=true \
-jar otoroshi.jar

[warn] otoroshi-env - The main datastore seems to be empty, registering some basic services
[info] otoroshi-env - The initial admin password has been written to '/etc/otoroshi/initial_admin_password'. Read it to log into the Otoroshi admin console.

:::warning Clean up When otoroshi.writeInitialAdminPasswordToFile is enabled, Otoroshi deletes the password file on the next reboot (and logs the deletion). Make sure you read it before restarting the instance. Also make sure the process is allowed to write to the configured path: if the file cannot be written, the error is logged but the boot is not interrupted. On environments where /etc/otoroshi is not writable (Docker, ...), set otoroshi.initialAdminPasswordFile to a writable location. :::

:::tip Security Whichever way you retrieve the initial password, when you log into the admin dashboard for the first time, Otoroshi asks you to create another account to avoid security issues. :::