2026-04-26·9 min read·sota.io team

Deploy Ada to Europe — EU Hosting for Ada Web Services in 2026

Ada is the programming language that the European aerospace and defence industry trusts with human lives. When an Airbus A380 pilot moves the sidestick, Ada software interprets that input and commands the flight control surfaces. When an Ariane rocket performs a trajectory correction burn, Ada code is running the guidance system. When Paris Métro line 14 — the fully automated line — decides whether to open its doors, Ada is the language executing that safety logic. This did not happen by accident. Ada was designed in Europe, by European engineers, for exactly this purpose.

Jean Ichbiah was a French computer scientist born in 1940 who studied at the École Polytechnique in Paris — the grande école that has produced a disproportionate share of France's technical elite. In the late 1970s, Ichbiah led the team at CII Honeywell Bull, a French computer manufacturer in Paris, that won the US Department of Defense competition to design a new programming language for embedded and real-time systems. The DoD had surveyed its software projects in 1974 and found over 450 different programming languages in use — a fragmentation that made maintenance and interoperability nearly impossible. They wanted one language, designed right, that could handle the demands of safety-critical systems.

Ichbiah's team submitted their design — called Green — in 1977. After two rounds of competition and independent evaluation, Green won. The language was standardized in 1983 as Ada 83, named after Ada Lovelace, and Jean Ichbiah received the ACM Distinguished Paper Award for its design. The language specification was published as an ANSI/ISO standard — a level of formal rigour unusual for programming languages of that era.

Ichbiah returned to France and continued refining the language until his death in 2007. Ada 95 added object-oriented programming. Ada 2005 and Ada 2012 added contracts and interfaces. The current standard, Ada 2022, adds more parallel programming constructs. Through all of it, the language's core commitments — strong typing, deterministic behaviour, explicit concurrency, provable correctness — have remained intact.

AdaCore, headquartered in Paris on the Rue du Faubourg Saint-Honoré, is the company that maintains GNAT — the GNU Ada Translator — which is the primary Ada compiler used worldwide. AdaCore was founded in 1994 and has grown into the central institution of the Ada ecosystem. GNAT Pro is the commercial Ada toolchain used by Airbus, Thales, SNCF, and hundreds of other European safety-critical software teams. The free community edition, GNAT Community Edition, makes Ada accessible for new projects. AdaCore's engineers contribute directly to the GCC Ada front-end, the SPARK formal verification tools, and the Ada Web Server library. Paris did not just invent Ada — Paris still maintains it.

The use of Ada in European infrastructure is not historical. It is present and ongoing. Airbus (Toulouse and Hamburg) uses Ada in the flight control computers of the A380, A350, and A320neo families. The fly-by-wire systems that replaced mechanical linkages between cockpit and control surfaces are Ada programs that execute in hard real-time with microsecond deadlines. Thales (Paris) uses Ada in avionics, railway signalling, and defence systems across the EU. The French Direction Générale de l'Armement specifies Ada for mission-critical defence software. RATP and SNCF use Ada in signalling systems for the Paris Métro and TGV network. The European Space Agency (technical centre in Darmstadt, Germany) has used Ada for spacecraft software since the 1990s — the Automated Transfer Vehicle that supplied the International Space Station was Ada software.

For EU development teams building safety-critical, high-reliability, or long-lifecycle services — industrial control backends, medical device APIs, infrastructure monitoring systems, defence-adjacent data processing — deploying on EU infrastructure is not just a compliance preference. It is often a contractual requirement. This guide shows how to deploy an Ada web service to sota.io.

Ada Web Server — AdaCore's Web Library

The Ada Web Server (AWS) is a production-quality library maintained by AdaCore that provides HTTP, HTTPS, WebSocket, REST API, and template rendering capabilities for Ada applications. Despite sharing initials with Amazon Web Services, AWS predates and has nothing to do with Amazon — it is an Ada library that has been in continuous development since the early 2000s.

For EU teams building REST APIs or data services in Ada, AWS provides:

Building an Ada Web Service

Project Structure

src/
  main.adb
  api-server.adb
  api-server.ads
  handlers/
    health_handler.adb
    health_handler.ads
    products_handler.adb
    products_handler.ads
ada_web_service.gpr
Dockerfile
sota.yaml

Project File

-- ada_web_service.gpr
with "aws";
with "gnatcoll";

project Ada_Web_Service is

   for Source_Dirs use ("src/**");
   for Object_Dir use ".obj";
   for Exec_Dir use "bin";
   for Main use ("main.adb");

   package Compiler is
      for Default_Switches ("Ada") use
        ("-g", "-O2", "-gnatwa", "-gnatVa", "-gnato");
   end Compiler;

   package Linker is
      for Linker_Options use ("-lpthread");
   end Linker;

end Ada_Web_Service;

HTTP Server Setup

-- src/api-server.ads
package API.Server is

   procedure Start (Port : Positive := 8080);
   procedure Stop;

end API.Server;
-- src/api-server.adb
with AWS.Server;
with AWS.Config;
with AWS.Response;
with AWS.Status;
with AWS.MIME;
with AWS.Dispatchers.Callback;
with Handlers.Health;
with Handlers.Products;

package body API.Server is

   WS : AWS.Server.HTTP;

   function Dispatcher
     (Request : AWS.Status.Data) return AWS.Response.Data
   is
      URI : constant String := AWS.Status.URI (Request);
   begin
      if URI = "/health" then
         return Handlers.Health.Handle (Request);
      elsif URI = "/products" or else URI'Length > 9
        and then URI (URI'First .. URI'First + 8) = "/products"
      then
         return Handlers.Products.Handle (Request);
      else
         return AWS.Response.Build
           (AWS.MIME.Application_JSON,
            "{""error"":""not found""}",
            Status_Code => AWS.Messages.S404);
      end if;
   end Dispatcher;

   procedure Start (Port : Positive := 8080) is
      Config : AWS.Config.Object := AWS.Config.Get_Current;
   begin
      AWS.Config.Set.Server_Port (Config, Port);
      AWS.Config.Set.Max_Connection (Config, 64);
      AWS.Server.Start
        (WS, "Ada Web Service",
         Callback => Dispatcher'Access,
         Config   => Config);
      AWS.Server.Wait (AWS.Server.Forever);
   end Start;

   procedure Stop is
   begin
      AWS.Server.Shutdown (WS);
   end Stop;

end API.Server;

Health Endpoint

-- src/handlers/health_handler.ads
with AWS.Response;
with AWS.Status;

package Handlers.Health is

   function Handle
     (Request : AWS.Status.Data) return AWS.Response.Data;

end Handlers.Health;
-- src/handlers/health_handler.adb
with AWS.Response;
with AWS.Status;
with AWS.MIME;

package body Handlers.Health is

   function Handle
     (Request : AWS.Status.Data) return AWS.Response.Data
   is
      pragma Unreferenced (Request);
   begin
      return AWS.Response.Build
        (AWS.MIME.Application_JSON,
         "{""status"":""ok"",""region"":""eu-central""}");
   end Handle;

end Handlers.Health;

Products REST Handler with PostgreSQL

-- src/handlers/products_handler.adb
with AWS.Response;
with AWS.Status;
with AWS.MIME;
with AWS.Messages;
with GNATCOLL.SQL.Postgres;
with GNATCOLL.SQL.Exec;
with Ada.Environment_Variables;

package body Handlers.Products is

   DB_Description : GNATCOLL.SQL.Exec.Database_Description;
   DB             : GNATCOLL.SQL.Exec.Database_Connection;

   procedure Connect_DB is
      DB_URL : constant String :=
        Ada.Environment_Variables.Value ("DATABASE_URL");
   begin
      DB_Description :=
        GNATCOLL.SQL.Postgres.Setup (Database => DB_URL);
      DB := DB_Description.Build_Connection;
   end Connect_DB;

   function Handle
     (Request : AWS.Status.Data) return AWS.Response.Data
   is
      Method : constant String :=
        AWS.Status.Method (Request);
   begin
      if DB = null then
         Connect_DB;
      end if;

      if Method = "GET" then
         declare
            Query  : constant GNATCOLL.SQL.Exec.Prepared_Statement :=
              GNATCOLL.SQL.Exec.Prepare
                ("SELECT id, name, price FROM products ORDER BY id",
                 On_Server => True);
            Cursor : GNATCOLL.SQL.Exec.Forward_Cursor;
            Result : GNATCOLL.Strings.XString;
         begin
            Cursor.Fetch (DB, Query);
            Result.Append ("[");
            while Cursor.Has_Row loop
               if not Cursor.First then
                  Result.Append (",");
               end if;
               Result.Append
                 ("{""id"":" & Cursor.Value (0)
                  & ",""name"":""" & Cursor.Value (1)
                  & """,""price"":" & Cursor.Value (2) & "}");
               Cursor.Next;
            end loop;
            Result.Append ("]");
            return AWS.Response.Build
              (AWS.MIME.Application_JSON, Result.To_String);
         end;
      else
         return AWS.Response.Build
           (AWS.MIME.Application_JSON,
            "{""error"":""method not allowed""}",
            Status_Code => AWS.Messages.S405);
      end if;
   end Handle;

end Handlers.Products;

Main Entry Point

-- src/main.adb
with API.Server;
with Ada.Text_IO;

procedure Main is
begin
   Ada.Text_IO.Put_Line ("Ada Web Service starting on port 8080");
   Ada.Text_IO.Put_Line ("Region: EU-Central (sota.io)");
   API.Server.Start (Port => 8080);
end Main;

Database Migration

Ada with GNATCOLL can run SQL migrations at startup:

-- src/migrations.adb
with GNATCOLL.SQL.Exec;
with Ada.Text_IO;

package body Migrations is

   procedure Run (DB : GNATCOLL.SQL.Exec.Database_Connection) is
   begin
      DB.Execute
        ("CREATE TABLE IF NOT EXISTS products (" &
         "  id SERIAL PRIMARY KEY," &
         "  name VARCHAR(255) NOT NULL," &
         "  price NUMERIC(10,2) NOT NULL DEFAULT 0," &
         "  currency VARCHAR(3) NOT NULL DEFAULT 'EUR'," &
         "  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()" &
         ")");
      Ada.Text_IO.Put_Line ("Migrations complete");
   end Run;

end Migrations;

Dockerfile for Production

Ada compiles to native binaries, which means the production Docker image can be extremely small — a statically linked Ada binary with no runtime dependencies:

# Stage 1 — Build
FROM ubuntu:24.04 AS builder

RUN apt-get update && apt-get install -y \
    gnat \
    gprbuild \
    libaws-dev \
    libgnatcoll-db2ada-dev \
    libgnatcoll-sql-dev \
    libssl-dev \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY . .

RUN gprbuild -P ada_web_service.gpr -j0

# Stage 2 — Minimal runtime
FROM ubuntu:24.04-minimal

RUN apt-get update && apt-get install -y \
    libaws22 \
    libgnatcoll-sql22 \
    libpq5 \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

RUN useradd -r -u 1001 adauser
USER adauser

COPY --from=builder /app/bin/main .

EXPOSE 8080

ENTRYPOINT ["./main"]

The resulting container is dramatically smaller than a JVM service. An Ada binary serving a REST API with PostgreSQL connectivity typically produces an image under 80MB, with an idle RSS memory footprint under 20MB. For EU teams running many microservices, this density matters operationally.

Deploying to Europe with sota.io

Add a sota.yaml to your project root:

# sota.yaml
build:
  dockerfile: Dockerfile

deploy:
  port: 8080
  region: eu-central
  health_check: /health

env:
  - DATABASE_URL

Deploy with:

sota deploy

sota.io builds the Ada binary in EU infrastructure, provisions a managed PostgreSQL instance in Frankfurt (same region as your service), and returns a live HTTPS URL — no Kubernetes, no VPCs, no load balancer configuration required.

Environment Variables

sota env set DATABASE_URL=postgresql://user:password@host:5432/dbname

Ada reads environment variables with the standard Ada.Environment_Variables package — no external dependency required.

SPARK: Formal Verification for the Most Critical EU Systems

Ada's most powerful feature for safety-critical EU deployments is SPARK — a formally verifiable subset of Ada maintained by AdaCore. SPARK allows developers to write mathematical proofs that a program cannot have buffer overflows, null pointer dereferences, race conditions, or arithmetic exceptions — at compile time, before the code runs.

This is why Ada (and SPARK) appears in:

For EU teams building backends that interface with any of these systems, or that process data with safety implications, SPARK Ada is the language that EU regulators understand and accept. No other mainstream programming language provides the same formal verification toolchain with industrial certification (DO-178C, EN 50128, IEC 62443).

GDPR and EU Data Residency

Ada's typical deployment contexts in Europe — aerospace, defence, railways, nuclear — all have data sovereignty requirements that go beyond GDPR. Defence data must remain in EU member state infrastructure by law. Aviation safety data must remain accessible to EU regulators. Medical device data is subject to MDR and IVDR.

sota.io's EU-native infrastructure — physically located in Frankfurt, Germany — satisfies not just GDPR Article 5's data minimisation requirements, but the more stringent data localisation requirements of EU defence, aviation, and medical frameworks. All data stays in Germany. No US cloud provider data transfer agreements are involved.

Performance Profile

Ada produces native binaries with deterministic performance characteristics — a property that safety-critical systems require. Unlike JVM or interpreted languages, Ada's execution time is bounded and predictable:

MetricAda (native)Java Spring BootNode.jsPython FastAPI
Startup time< 50ms4-8s300-800ms500ms-2s
RSS memory (idle)15-25MB280-350MB60-120MB80-150MB
First request latency< 1ms200-800ms5-20ms10-50ms
Throughput (req/s)40,000-80,0008,000-12,00015,000-25,0003,000-8,000
Binary/image size20-80MB280MB (JRE)120MB200MB

Ada's throughput numbers reflect the compiler's aggressive optimisation — GNAT generates code comparable to GCC C++ in performance, because it uses the same GCC backend. For EU services under latency or memory constraints, Ada's native compilation is a genuine advantage.

Deployment Comparison

Featuresota.ioRailwayRenderFly.io
EU-native infrastructure✓ Frankfurt✗ US-default✓ Frankfurt✓ EU regions
GDPR Article 28 DPAPartialPartial
Managed PostgreSQL EU✓ same region
Ada/GNAT auto-detect✓ via Dockerfile✓ via Dockerfile
SPARK-ready toolchain✓ (custom image)
Data stays in EU✓ alwaysPartialPartial
Defence/medical compliance

Ada was invented by a French engineer, maintained by a French company, and powers the safety-critical infrastructure of the European continent. The language was designed to run where failure is not acceptable — in aircraft, spacecraft, trains, and hospitals. Deploying an Ada service to European infrastructure is not a technical curiosity. It is a return to origin: a language built in Paris, running in Paris-connected data centres, serving the European systems it was created to protect.

See Also

Deploy your Ada service to Europe with sota.io →