In this section we assume you have set up your local machine with the prerequisites required and found on Pre-Setup


  1. Clone the CitrineOS repository to your local machine:

     git clone https://github.com/citrineos/citrineos-core
  2. Navigate to the citrineos-core/Server directory and run the entire required stack with docker-compose.

     cd citrineos-core/Server
     docker-compose up -d 

    The expected outcome should look like this:

     [+] Running 5/6
    - Network server_default          Created                                 28.0s
    ✔ Container server-amqp-broker-1  Healthy                                19.3s
    ✔ Container server-ocpp-db-1      Healthy                                11.4s
    ✔ Container server-redis-1        Healthy                                16.3s
    ✔ Container server-directus-1     Healthy                                27.1s
    ✔ Container server-citrine-1      Started                                27.7s

After Setup

You now have a running CitrineOS server plus the supporting infrastructure, in this case:

Please consider that this setup is the development environment and do not simply deploy it to an exposed environment with initial passwords!


We recommend running and developing the project with the docker-compose set-up.

However, if you like to rather run it locally and need to adjust where the server is connecting to, please locally (only) adjust the configuration file at ./Server/src/config/envs/local.ts

You can now use the npm run command to start with your environment setup:

npm run start-unix

You may run into issues attempting this on a Windows DOS command line, in which case npm run start-windows is available. Both of these commands set the APP_NAME and APP_ENV environment variables; if you wish to set those yourself, you can use npm run start-docker.


Connecting a Charger to CitrineOS

If you want to now connect a charger to CitrineOS, you can do so without any set up, just point the charger to ws://localhost:8081. Depending on the charger you are using, you may need to append the station id to the url like so ws://localhost:8081/<stationId>. Some chargers take care of this automatically.

In Directus, the Boot table can be used to both review the most recent boot status as well as set a boot status for the next BootNotificationRequest received from the charging station. After a successful boot, the status is set to Accepted. If you wish to fetch the device model from the charger as part of the boot process described in the B02 use case of part 2 of the OCPP 2.0.1 protocol, set the status to Pending and check the ‘Get Base Report On Pending’ option. This will cause the next boot to be responded to with a BootNotificationResponse that has status Pending, then CitrineOS will send a GetBaseReportRequest, triggering a series of NotifyReportRequest messages. After the full report has been sent, the next attempted boot by the charger will be Accepted.

The Boot table has CRUD endpoints in order to be manipulated via REST API, of which the docs can be found here (if you have CitrineOS running locally): localhost:8080/docs Here is an example request for charge point cp001:

curl --location --request PUT 'http://localhost:8080/data/configuration/boot?stationId=cp001' \
--header 'Content-Type: application/json' \
--data '{ "status": "Pending" }'

Using Security Profile 1

There are two layers of security available before the charger has the opportunity to send an OCPP message such as BootNotificationRequest. When the charger attempts to connect, it can be rejected at the transport layer if the websocket server is using security profiles 2 or 3. Otherwise, the http upgrade request occurs. The charger’s upgrade request can be rejected if:

Once a charger has a Charging Station entry and its password has been set, you can connect it to the security profile 1 websocket server at ws://localhost:8082.

If you want to add a password for security profile 1 and 2, send the following request to the CitrineOS API.

curl --location --request POST 'localhost:8080/data/configuration/password?callbackUrl=csms.pro/api/notifications' \
--header 'Content-Type: application/json' \
--data '{
  "stationId": "cp001",
  "password": "DbkTu048=xueA69@-dB94hjV*fGdyQOzgVuhhH1L",
  "setOnCharger": false

Please note that the password should be sent in plain text and should not be hashed. Providing a password is optional. If you do not provide one, it will be automatically generated. If you do provide a password, it must comply with the OCPP specification.

Set setOnCharger to true if the password has already been updated on the charger and you only need to update the device model associated with the station id. If you need to update the password on the charger (OCPP A01 use case), set setOnCharger to false.


In the case you don’t have a charger that supports OCPP 2.0.1 to experiment with, we can recommend using the Linux Foundation Energy project EVerest. See here for the repository. They have built an open source version of charger firmware and also allow for using it as a simulator. They support OCPP 2.0.1 which makes it a great testing opportunity with CitrineOS. For the long route of setting up EVerst you can follow their documentation and build the project yourself.See here for Docs

As a short-cut you can also use their demo repository that hosts a Docker packaged EVerest image. See here for Github Repo

If you want to run EVerest on the side while developing and making changes, you can follow the steps below.

  1. Run your citrine instance locally with docker compose up -d in the Citrine repository
  2. Clone the EVerest Demo repository and cd into the repo
  3. With citrine running excute an “add charger” script at ./citrineos/add-charger.sh This adds a charger, location and password for the charge to Citrine
  4. Bring up EVerest with docker compose --project-name everest-ac-demo --file "docker-compose.ocpp201.yml" up -d
  5. Copy over the appropriate device model with docker cp manager/device_model_storage_citrineos_sp1.db \ everest-ac-demo-manager-1:/ext/source/build/dist/share/everest/modules/OCPP201/device_model_storage.db
  6. Start EVerst having OCPP2.0.1 support with docker exec everest-ac-demo-manager-1 sh /ext/source/build/run-scripts/run-sil-ocpp201.sh

Making changes to CitrineOS

As mentioned above, we recommend using the docker-compose set-up to have a stable and reproducible environment. In the docker-compose file we wire up the local Citrine server directory to be used. This means, the docker container running with the server is using the files that are on your machine. We start the CitrineOS sever with nodemon src/index.ts so If you make adjustments to your local files, it will get picked up and hot-reloaded in the container.

Make sure if you’ve made code changes since you’ve last run docker compose up that you run docker compose down && docker compose build before running docker compose up again to ensure your changes are reflected in the image docker uses when starting up the CitrineOS container.