Deploy a Micronaut Function (Serverless) application to Oracle Cloud

Learn how to deploy a Micronaut Function (Serverless) application to Oracle Cloud.

Authors: Burt Beckwith

Micronaut Version: 3.7.3

1. Getting Started

In this guide, we will create a Micronaut application written in Java.

2. What you will need

To complete this guide, you will need the following:

  • Some time on your hands

  • A decent text editor or IDE

  • JDK 11 or greater installed with JAVA_HOME configured appropriately

  • Docker installed

  • A paid or free trial Oracle Cloud account (create an account at

  • Oracle Cloud CLI installed with local access to Oracle Cloud configured by running oci setup config

3. Solution

We recommend that you follow the instructions in the next sections and create the application step by step. However, you can go right to the completed example.

4. Writing the App

Create an application using the Micronaut Command Line Interface or with Micronaut Launch.

mn create-function-app --features=graalvm,oracle-function example.micronaut.micronautguide --build=gradle --lang=java --jdk=11
If you don’t specify the --build argument, Gradle is used as the build tool.
If you don’t specify the --lang argument, Java is used as the language.

If you use Micronaut Launch, select "Function Application for Serverless" as application type, JDK version 11 or higher, and add the graalvm and oracle-function features.

The previous command creates a Micronaut application with the default package example.micronaut in a directory named micronautguide.

If you have an existing Micronaut application and want to add the functionality described here, you can view the dependency and configuration changes from the specified features and apply those changes to your application.

4.1. Enable annotation Processing

If you use Java or Kotlin and IntelliJ IDEA, make sure to enable annotation processing.


The generated application contains a Function class with a simple Oracle Cloud API call to retrieve the tenancy OCID from the injected TenancyIdProvider bean. That will be sufficient to ensure that the deployed function works correctly:

package example.micronaut;

import io.micronaut.core.annotation.ReflectiveAccess;
import io.micronaut.oraclecloud.core.TenancyIdProvider;
import io.micronaut.oraclecloud.function.OciFunction;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import java.util.Date;

public class Function extends OciFunction {

    TenancyIdProvider tenantIdProvider;

    @ReflectiveAccess (1)
    public String handleRequest() {
        String tenancyId = tenantIdProvider.getTenancyId();
        return "Your tenancy is: " + tenancyId;
1 This is needed because Project.fn invokes the function handler via reflection

5. Testing the Application

To run the tests:

./gradlew test

Then open build/reports/tests/test/index.html in a browser to see the results.

6. Configuring Oracle Cloud Resources

We need to configure some cloud infrastructure to support deploying functions.

First, login to your Oracle Cloud tenancy as an administrator, or a user with sufficient permissions to create the infrastructure described below.

6.1. Compartment

Use an existing compartment to create your function in, or create a new one by opening the Oracle Cloud Menu and clicking "Identity & Security", and then "Compartments"

See the Compartments docs for more information.

6.2. Function user and group

Create a group for Oracle Functions users by clicking the Oracle Cloud menu and selecting "Identity & Security", and then click "Groups":


Click "Create Group":


Choose a name and a description, e.g. "mn-function-group", and click "Create":


Create a user by clicking the Oracle Cloud menu and selecting "Identity & Security", and then click "Users":


Click "Create User":


Choose a name and a description, e.g. "mn-function-user", and click "Create":


Scroll down and click "Add User to Group":


Select the group you created and click "Add":


You’ll need an auth token to authenticate to the container registry that will contain the Docker images for your functions. Click "Auth Tokens" and then "Generate Token":


Enter a name for the token, e.g. "mn-ocir", and click "Generate Token":


Copy the token to the clipboard and save it for later:


You will need a password to login as this user, so click the "Create/Reset Password" button:


and then click the "Create/Reset Password" button in the dialog:


Click the "Copy" link and save the generated password for later.

See the Groups and Users docs for more information.

6.3. Oracle Cloud Infrastructure Registry (OCIR) repository

Your function will be deployed as a Docker image, so you need to choose a repo name in your tenancy. The repo will be created for you the first time you push an image to it.

The name can be simple, e.g. "mn-functions", or you can add a path prefix to help group the repos when there are many of them, e.g. "my-username/my-repo/mn-functions"

Update your build script with the location to deploy the Docker container.

Edit build.gradle and replace REGION, TENANCY, and REPO with appropriate values:

dockerBuild {
    images = ["[REGION][TENANCY]/[REPO]/$$project.version"]

For REGION, use the lowercase value from the "Region Key" column in Regions and Availability Domains, e.g. iad.

For TENANCY, use the Object Storage namespace string of the tenancy (as shown on the Tenancy Information page), e.g. my-tenancy.

For REPO, use the name of the repository to use, e.g. mn-functions (or my-username/my-repo/mn-functions).

The final value should look something like this:

dockerBuild {
    images = ["$$project.version"]

6.4. OCIR authentication

Login to OCIR by running:

docker login <region-key>

Replace <region-key> with the value you used for REGION above, e.g.

For the username, enter <tenancy-namespace>/<username>, e.g. my-tenancy/mn-function-user. If your tenancy is federated with Oracle Identity Cloud Service, use the format <tenancy-namespace>/oracleidentitycloudservice/<username>.

For the password, use the auth token you copied earlier.

See the OCIR login docs for more information.

6.5. Virtual Cloud Network (VCN) and Subnet

If you have a suitable VCN and subnet you can use those, or create new ones.

To create a VCN, open the Oracle Cloud Menu and click "Networking", then "Virtual Cloud Networks":


Click "Start VCN Wizard":


Select "VCN with Internet Connectivity" and click "Start VCN Wizard":


Enter a name for the VCN, e.g. "mn-functions-vcn", and change the CIDR block values if needed:


Review the settings and click "Create":


See the VCN and Subnets docs for more information.

6.6. Policies

We’ll need to create some policies to grant various function-related permissions.

Open the Oracle Cloud Menu and click "Identity & Security", and then "Policies":


Select the root compartment from the drop-down and click "Create Policy":


Choose a name and description, e.g. "mn-functions-root-policy", and click "Show Manual Editor". Copy the following and paste it into the "Policy Builder" field, replacing "<group-name>" with the name of the group created earlier (e.g. "mn-function-group") and "<compartment-name>" with the name of the compartment you’re using, and click "Create":

Allow group <group-name> to manage repos in tenancy
Allow group <group-name> to read objectstorage-namespaces in tenancy
Allow group <group-name> to manage logging-family in compartment <compartment-name>

Create another policy in the compartment where your function will be and choose a name and description, e.g. "mn-functions-compartment-policy". Copy the following and paste it into the "Policy Builder" field, again replacing "<group-name>" and "<compartment-name>", and click "Create":

Allow group <group-name> to manage functions-family in compartment <compartment-name>
Allow group <group-name> to read metrics in compartment <compartment-name>
Allow group <group-name> to use virtual-network-family in compartment <compartment-name>
Allow group <group-name> to use apm-domains in compartment <compartment-name>
Allow service faas to use apm-domains in compartment <compartment-name>

See the Policies docs for more information.

7. Creating the function

First we’ll need to build the function as a Docker image and push it to the OCIR repository.

From the demo project directory, run:

./gradlew dockerPush

Once you’ve pushed the Docker container, create the function in the console. First, log out from your administrator account and log in as the user created above.

Open the Oracle Cloud Menu and click "Developer Services", and then "Applications" under "Functions":


Click "Create Application":


Choose a name for the application, e.g. "mn-guide-function-app", and select the VCN created earlier. Select the private subnet, and click "Create":


Click "Functions" (under "Resources") in the lower left, and then click "Create Function":


Choose a name for the function, e.g. "mn-guide-function", select the repository where you pushed the Docker image, and select the uploaded image. Select 512MB memory and click "Create":


Click the function link in the list, and click the "Copy" link in the OCID row; you’ll need the OCID of the function to invoke the function:


8. Enable Tracing and Logs

Open the Oracle Cloud Menu and click "Observability & Management", and then "Administration" under "Application Performance…​":


Click "Create APM Domain":


Choose a name and description, e.g. "mn-function-apm", the compartment (and optionally check "Create as Always Free Domain"), then click "Create":


Navigate back to the function application page and click "Logs" (under "Resources") in the lower left:


Click the slider to enable logs, then choose the compartment to store them in, a log group, a log name, and the retention policy, and click "Enable Log":


Next, click "Traces" in the lower left:


Click "Configure", then choose the compartment, and the APM domain created earlier, and click "Enable Trace":


Finally, navigate to the application’s function page and click the button to enable function tracing:


See the Logging and Tracing docs for more information.

9. Invoking the function

We’ll use the OCI command line to invoke the function. If you haven’t already, install the Oracle Cloud CLI and run oci setup config.

Run the following, replacing <OCID> with the OCID of the function you copied above:

oci fn function invoke --function-id <OCID> --file "-" --body ""

The output should look something like:

Your tenancy is: ocid1.tenancy.oc1..aaaaaaaaud4g4e5ovjawn5cij7ke.................

The first invocation ("cold start") will take a while as the infrastructure is configured, probably 10-20 seconds or more but subsequent invocations should return in 1-2 seconds.

10. Deploying as a Native Executable

10.1. Install GraalVM

We will use GraalVM, the polyglot embeddable virtual machine, to generate a Native executable of our function.

Compiling native executables ahead-of-time with GraalVM improves startup time and reduces the memory footprint of JVM-based applications and functions.

Only Java and Kotlin projects support using GraalVM’s native-image tool. Groovy relies heavily on reflection, which is only partially supported by GraalVM.

The easiest way to install GraalVM on Linux or Mac is to use

Java 11
sdk install java 22.1.0.r11-grl
If you still use Java 8, use the JDK11 version of GraalVM.
Java 17
sdk install java 22.1.0.r17-grl

For installation on Windows, or for manual installation on Linux or Mac, see the GraalVM Getting Started documentation.

After installing GraalVM, install the native-image component, which is not installed by default:

gu install native-image

10.2. Building and deploying the native executable

Deploying the function as a native executable is similar to the earlier deployment above.

First you need to update your build script with the location to deploy the native executable Docker container.

Edit build.gradle like before, but set the images property in the dockerBuildNative block this time, replacing REGION, TENANCY, and REPO as before:

dockerBuildNative {
    images = ["[REGION][TENANCY]/[REPO]/$$project.version"]

Since it’s unlikely that you’ll be deploying both jar-based containers and native executable-based containers, you can use the same repo:

dockerBuildNative {
    images = ["[REGION][TENANCY]/[REPO]/$$project.version"]

Next, update the version.

Edit build.gradle and increment the version to 0.2:

version = "0.2"

Depending on the Micronaut version you’re using, you might also need to update some properties in your build script to update the Docker configuration.

In your build.gradle, if the graalvmNative block includes --static in the args list, replace it with -H:+StaticExecutableWithDynamicLibC. Also change the base image to frolvlad/alpine-glibc:alpine-3.12 in the dockerfileNative block:

graalvmNative {
    binaries {
        main {

dockerfileNative {

Then from the demo project directory, run:

./gradlew dockerPushNative

Once you’ve pushed the Docker container, edit the function in the console to use the new container, and to reduce the memory to 128MB:


Use the same OCI command as before to invoke the function. No changes are needed because the function OCID doesn’t change when deploying new containers.

11. Next steps

Explore more features with Micronaut Guides.

Read more about the Micronaut Oracle Cloud integration.

Also check out the Oracle Cloud Function documentation for more information on the available functionality.