Documentation · EV Supervision Lite

Deploy EV Supervision Lite on Oracle Cloud

This guide walks you from an empty Oracle network to your first connected charger. One path, the one we run in production: a virtual machine, Docker Compose, a managed PostgreSQL database and automatic HTTPS.

Written for someone comfortable in a Linux terminal. Every step tells you what you should see before you move on.

What you're about to install

Five pieces work together. You don't need to know them inside out, but knowing who does what helps a lot on the day something gets stuck.

  • CaddyThe reverse proxy, in other words the front door of your server. It gets and renews HTTPS certificates on its own (Let's Encrypt), then sends each request to the right service.
  • The server (ev-server)The heart of the product, written in Kotlin. It holds the permanent OCPP 1.6 connection with each charger, works out sessions and talks to Stripe. It listens on port 8080, never exposed directly.
  • The dashboard (ev-dashboard)The web app where your team manages chargers, users, RFID cards and rates. It listens on port 3000 and passes calls to the server over the private network.
  • PostgreSQLThe database. We recommend the managed OCI Database with PostgreSQL service, in a private subnet, with its automatic backups.
  • OCI VaultOracle's vault, where you keep the secrets: the database password and the encryption key.

Before you start

  • An Oracle Cloud account and a compartment where you can create a network, a database and an instance. Suggested region: eu-paris-1 or eu-marseille-1.
  • The oci CLI installed and configured on your computer, or Oracle's web console if you'd rather click.
  • A domain name whose DNS you control, for example charging.your-domain.com.
  • An SSH key, for example ~/.ssh/id_ed25519.pub.
  • At least one charger that speaks OCPP 1.6 JSON, for the final test.
  • Optional: a Stripe account for payments, and a sender address on your domain for email.
  • The Codetics attribution licence, read and accepted. It allows commercial use, as long as the Codetics attribution stays visible.
01

Prepare Oracle Cloud

First, the ground to build on: a network where the instance is reachable from the internet and the database stays out of sight.

  1. 1.1Create the network

    In your compartment, create a VCN (Oracle's virtual private network) with two subnets: a public one for the instance and a private one for the database. That way the database has no address visible from the internet.

    In the security list, allow ports 80 and 443 from the internet to the instance, and port 5432 from the public subnet to the private one. Port 80 is used to obtain HTTPS certificates, 443 serves the dashboard and the chargers, 5432 is PostgreSQL.

    What you should see Your VCN has a public and a private subnet, and the security list shows the 80, 443 and 5432 rules.

  2. 1.2Pick the instance shape

    For a few hundred chargers, the product guide suggests a VM.Standard.E5.Flex instance with 1 OCPU and 8 GB of memory. For a trial, a VM.Standard.A1.Flex (Arm Ampere processor) with 1 OCPU and 6 GB is enough, together with the local database described in the next chapter.

    One OCPU is two virtual CPUs on x86 and one core on Ampere. Oracle's cost estimator gives you the exact price for your region.

    Watch out On an Arm instance, don't run images built for amd64: they stop with "exec format error". If you follow this guide, docker compose builds the images on the instance itself, so in the right architecture.

02

Database and secrets

The database holds everything: chargers, sessions, users, payments. A managed service means you don't have to think about PostgreSQL backups and upgrades.

  1. 2.1Create the PostgreSQL database

    Create an OCI Database with PostgreSQL, version 16 or later, 2 OCPU shape, in the private subnet. Turn on daily backups with at least 7 days of retention.

    Then create the ev_supervision database and an ev_supervision user that owns it. This matters: on first start the server creates all the tables itself, so it has to own the database. Write down the database's private endpoint, you'll need it for the configuration.

    For a quick trial you can skip the managed service: Docker Compose can run PostgreSQL on the instance, in a local volume. That's the local-db profile, covered in the Configure chapter.

    What you should see The console shows the database as active, with a private address in your private subnet.

  2. 2.2Generate and store the secrets

    Two secrets are essential: the database password and the APP_ENCRYPTION_KEY, which protects sensitive data in the database. Generate each one with the command below, run twice.

    Store them in an OCI Vault, with a master key, as database-password and app-encryption-key. If the instance should read these secrets itself, put it in a dynamic group allowed to read secret-bundles in the compartment.

    Terminal
    openssl rand -base64 32

    What you should see A 44-character string appears each time you run it.

    Watch out Without APP_ENCRYPTION_KEY, the encrypted data in the database can no longer be read. Keep an offline copy, separate from the database backups.

03

Create the instance and get the code

Now for the machine that will run the server, the dashboard and Caddy.

  1. 3.1Launch the instance

    From your computer, launch the instance in the public subnet, with a public IP and your SSH key. Replace <compartment>, <availability-domain>, <Ubuntu 24.04 image> and <public-subnet> with the OCIDs from your account. You can do exactly the same in the web console.

    Terminal
    oci compute instance launch --compartment-id <compartment> \
      --availability-domain <availability-domain> --shape VM.Standard.E5.Flex \
      --shape-config '{"ocpus":1,"memoryInGBs":8}' --image-id <Ubuntu 24.04 image> \
      --subnet-id <public-subnet> --assign-public-ip true \
      --display-name ev-supervision --ssh-authorized-keys-file ~/.ssh/id_ed25519.pub

    What you should see The ev-supervision instance reaches the running state and shows a public IP address. You can SSH into it.

  2. 3.2Open the system firewall

    OCI's Ubuntu and Oracle Linux images filter incoming ports inside the operating system too, on top of the security list. On Ubuntu, open 80 and 443 in /etc/iptables/rules.v4. On Oracle Linux, use firewall-cmd.

    Watch out This is the most common trap on Oracle: the security list allows 80 and 443, but the system keeps them closed. HTTPS certificates can't be issued and chargers can't connect.

  3. 3.3Install Docker and get the code

    On the instance, install Docker Engine with the Compose plugin, following Docker's official instructions for Ubuntu.

    Before cloning, read the Codetics attribution licence in the repository: cloning it means you accept that licence. Then clone it and move into the deploy folder, where you'll run every command that follows.

    Terminal
    git clone https://github.com/codetics-software/ev-server-lite ev-supervision && cd ev-supervision/deploy

    What you should see The deploy folder contains docker-compose.yml, .env.example, server.env.example and the caddy folder, among others.

04

Configure the installation

The whole configuration fits in two files: .env, read by Docker Compose, and server.env, passed to the server. Everything else (internal addresses, trusted proxies, port 8080) is already set in docker-compose.yml, so you can leave it alone.

  1. 4.1Point your domain at the instance

    With your DNS provider, create a record pointing your name, for example charging.your-domain.com, at the instance's public IP. Do it before the first start: Caddy needs it to get the Let's Encrypt certificate.

    What you should see ping or dig on your name returns the instance's public IP.

  2. 4.2Copy the example files

    Both examples are your starting point, with comments in French.

    Terminal
    cp .env.example .env
    cp server.env.example server.env
  3. 4.3Fill in .env

    Put your domain name in APP_HOST, and in ACME_EMAIL the address that should receive Let's Encrypt notices. Empty COMPOSE_PROFILES to use the managed database, then set DATABASE_URL with the private endpoint you wrote down and DATABASE_PASSWORD with the database-password secret.

    For a trial with the local database, keep COMPOSE_PROFILES=local-db and leave DATABASE_URL unset. The OCPP_HOST line is only for chargers on security profile 3: leave it empty.

    deploy/.env
    APP_HOST=<your-domain>
    OCPP_HOST=
    ACME_EMAIL=<operations-email>
    COMPOSE_PROFILES=
    DATABASE_URL=jdbc:postgresql://<private endpoint>:5432/ev_supervision?sslmode=require
    DATABASE_USER=ev_supervision
    DATABASE_PASSWORD=<database-password secret>
    JAVA_TOOL_OPTIONS=-XX:MaxRAMPercentage=75 -XX:+ExitOnOutOfMemoryError
  4. 4.4Fill in server.env

    The first admin account is created on first start, and only if the database has no users yet. BOOTSTRAP_TENANT is your workspace ID, which you'll type every time you sign in, so replace codetics with your own now. Pick a long passphrase for BOOTSTRAP_PASSWORD.

    Paste the app-encryption-key into APP_ENCRYPTION_KEY. OCPP_PUBLIC_URL is the address shown on each charger's commissioning sheet: wss:// followed by your domain and /ocpp. Leave the Stripe and email blocks empty for now, we'll get to them.

    deploy/server.env
    BOOTSTRAP_TENANT=<workspace-id>
    BOOTSTRAP_EMAIL=<admin-email>
    BOOTSTRAP_PASSWORD=<long-passphrase>
    
    APP_ENCRYPTION_KEY=<app-encryption-key secret>
    OCPP_PUBLIC_URL=wss://<your-domain>/ocpp

    Watch out Never commit server.env to a Git repository or paste it into a message: it holds your secrets.

05

Start it and sign in

The moment you've been waiting for. The first start builds the images on the instance, so it takes a little longer than the ones after it.

  1. 5.1Start the services

    From the deploy folder, build and start everything, then follow the server logs. On first start the server creates the tables in the database and the admin account.

    Terminal
    docker compose up -d --build
    docker compose logs -f server

    What you should see The logs scroll, then settle down with no errors. Leave with Ctrl+C: the services keep running.

  2. 5.2Sign in to the dashboard

    Open https://<your-domain>/auth/login in your browser. The sign-in screen asks for three things: the workspace ID (your BOOTSTRAP_TENANT), the admin email and the passphrase. The dashboard itself is in French.

    Once you're in, open Espace client (workspace settings) to name your workspace and upload your logo. It will show in the dashboard and on the payment portal.

    What you should see The browser shows a padlock (valid certificate) and you land in your workspace's dashboard.

  3. 5.3Remove the first-run credentials

    The account now lives in the database. Delete BOOTSTRAP_PASSWORD (and BOOTSTRAP_EMAIL) from server.env, then restart so the server runs without them.

    Terminal
    docker compose up -d --build

    What you should see You can still sign in normally with your admin account.

06

Connect a charger

A charger opens a permanent connection to your server and keeps it open. All it needs is an address and a password.

  1. 6.1Add the charger

    In the dashboard, add the charger with its ID. Its password is shown only once, so copy it right away. If you lose it, the charger's page lets you generate a new one.

  2. 6.2Configure the charger

    In the charger's configuration screen, choose OCPP 1.6 JSON, the ocpp1.6 subprotocol and Basic authentication. <workspace> is your workspace ID, <charger-id> the ID you just added. The charger usually builds the Authorization header itself from the ID and password you enter.

    Charger settings
    wss://<your-domain>/ocpp/<workspace>/<charger-id>
    Authorization: Basic <base64(charger-id:password)>

    What you should see The charger shows as online in the dashboard. If it stays offline, open its history: a CONNECTION_REJECTED event means the connection was refused, often because of a wrong password or address.

07

Payments with Stripe

Optional. Stripe enables QR code payment without an account, driver billing and charging card sales. Each workspace connects its own Stripe account, and the money lands straight in it.

  1. 7.1Add the keys

    Start with Stripe's test keys, then switch to live keys once everything checks out. In Stripe, create an event destination for connected accounts, pointing at the address below, with the seven events listed. Stripe then gives you the signing secret to put in STRIPE_WEBHOOK_SECRET.

    Webhook address
    https://<your-domain>/api/backend/api/v1/stripe/webhook
    Events
    checkout.session.completed
    checkout.session.async_payment_succeeded
    checkout.session.async_payment_failed
    checkout.session.expired
    invoice.paid
    invoice.payment_failed
    invoice.finalized
    deploy/server.env
    STRIPE_SECRET_KEY=<Stripe secret key>
    STRIPE_WEBHOOK_SECRET=<signing secret>
    STRIPE_PUBLISHABLE_KEY=<Stripe publishable key>
  2. 7.2Restart and turn on public payment

    Restart with docker compose up -d --build. In the dashboard, create a rate in euros under Tarifs (rates), connect the workspace's Stripe account under Paiements (payments) and finish verification with Stripe, then turn on public payments.

    Under Points de charge publics (public charge points), link a charger, a connector and a rate, publish the portal and download its QR code. Drivers scan it, pick a credit between €5 and €100 and pay, without creating an account.

    What you should see In Stripe, the destination's deliveries tab shows a test event delivered successfully.

08

Email

Optional but handy: password resets, alerts and notifications all go out by email.

  1. 8.1Send through OCI Email Delivery

    On Oracle, outbound port 25 is blocked by default. Use OCI Email Delivery over SMTP on port 587 with STARTTLS. Set up SPF, DKIM and DMARC for the sender address, or your emails may end up in spam.

    Fill in the email block of server.env with the SMTP server and credentials Email Delivery gives you, then restart with docker compose up -d --build.

    deploy/server.env
    MAIL_ENABLED=true
    MAIL_FROM=<sender-address>
    SMTP_HOST=<Email Delivery SMTP server>
    SMTP_PORT=587
    SMTP_USER=<SMTP user>
    SMTP_PASSWORD=<SMTP password>
    SMTP_AUTH=true
    SMTP_STARTTLS=true

    What you should see Request a password reset from the sign-in screen: the email arrives.

    Watch out Outbound port 25 is blocked by default on Oracle. Don't try to send directly, go through port 587.

09

Check, back up, update

Your platform is running. A few checks before you connect the whole fleet, then the two habits that keep it healthy.

  1. 9.1Check from the internet

    Run these three commands from your computer. Replace <your-domain> and <workspace>. UNKNOWN-CHARGER can stay as it is.

    Terminal
    curl -sSI https://<your-domain>/auth/login
    curl -sS -o /dev/null -w '%{http_code}\n' https://<your-domain>/actuator/health
    curl --http1.1 -sS -o /dev/null -w '%{http_code}\n' \
      -H 'Connection: Upgrade' -H 'Upgrade: websocket' -H 'Sec-WebSocket-Version: 13' \
      -H 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' -H 'Sec-WebSocket-Protocol: ocpp1.6' \
      https://<your-domain>/ocpp/<workspace>/UNKNOWN-CHARGER

    What you should see The sign-in page answers 200, /actuator/health answers 404 (it must not be visible from the internet) and the charger connection without credentials answers 401. Then a test charger should stay connected for more than an hour with no reconnection in its history.

  2. 9.2Back up

    With the managed database, daily backups are already on. Add an exported copy outside your Oracle account, and try a restore at least once a quarter on a separate environment. Keep APP_ENCRYPTION_KEY in the vault and as an offline copy.

    With the local database, the command below produces a full copy of the database. Keep it off the instance.

    Terminal
    docker compose exec postgres pg_dump -U ev_supervision ev_supervision
  3. 9.3Update

    A new version goes in two moves: pull the code, rebuild. Database migrations run on start, so back up the database before each update and pick a quiet moment.

    During the restart, chargers disconnect and then reconnect on their own. The messages they held back are replayed, and duplicates are recognised.

    Terminal
    git pull && docker compose up -d --build

    What you should see Chargers come back online in the dashboard after the restart.

10

The variables you set

Only the ones in this guide. The example files hold others (mobile apps, roaming, advanced security profiles) that you can leave as they are.

VariableWhat it doesStatus
APP_HOSTYour domain name (.env). Used by the dashboard, the API, Stripe notifications and the chargers.supervision.exemple.frRequired
ACME_EMAILContact address for Let's Encrypt certificates (.env).exploitation@exemple.frRequired
COMPOSE_PROFILESEmpty with the managed database. local-db runs PostgreSQL on the instance, for a trial (.env).Required
DATABASE_URLAddress of the managed database (.env). Not needed with the local database.jdbc:postgresql://10.0.0.5:5432/ev_supervision?sslmode=requireRequired
DATABASE_USERUser that owns the database (.env).ev_supervisionRequired
DATABASE_PASSWORDDatabase password, stored in OCI Vault (.env).Required
BOOTSTRAP_TENANTID of the first workspace, typed at sign-in. Choose it before the first start (server.env).codeticsRequired
BOOTSTRAP_EMAILEmail of the first admin (server.env). Remove it after your first sign-in.administrateur@exemple.frRequired
BOOTSTRAP_PASSWORDPassphrase of the first admin (server.env). Remove it after your first sign-in.Required
APP_ENCRYPTION_KEY32 random bytes in Base64 that encrypt secrets in the database (server.env). Needed for Stripe. Losing it makes that data unreadable.Required
OCPP_PUBLIC_URLCharger address shown on each charger's commissioning sheet (server.env).wss://supervision.exemple.fr/ocppOptional
STRIPE_SECRET_KEYStripe secret key, test first, then live (server.env).Optional
STRIPE_WEBHOOK_SECRETSigning secret of the Stripe event destination (server.env).Optional
STRIPE_PUBLISHABLE_KEYStripe publishable key, needed for drivers to save payment methods (server.env).Optional
MAIL_ENABLEDTurns email sending on (server.env).trueOptional
MAIL_FROMSender address, on your domain (server.env).supervision@exemple.frOptional
SMTP_HOSTSMTP server given by OCI Email Delivery (server.env).Optional
SMTP_PORTSMTP port. Port 25 is blocked on Oracle (server.env).587Optional
SMTP_USERSMTP user given by Email Delivery (server.env).Optional
SMTP_PASSWORDSMTP password given by Email Delivery (server.env).Optional
Other hosts

Another host? We'll help

This guide covers Oracle Cloud on a single machine. Would you rather use another host, a Kubernetes cluster, a load balancer in front of the instance, or chargers on security profile 3 with client certificates (mTLS)? Tell us about your setup.

We can deploy the platform for you, or stay by your side while you install it. Either way, the platform remains yours.

  • AWS
  • Google Cloud
  • Microsoft Azure
  • OVHcloud
  • Scaleway
  • Hostinger
  • Cloudflare
  • Vercel
  • Your own server