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

Deploy Julia to Europe — EU Hosting for Scientific Computing in 2026

Julia was created at MIT in 2012 by Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and Alan Edelman. The language was designed to solve what its authors called the two-language problem: scientists and engineers write fast, expressive prototypes in Python or MATLAB, then spend months rewriting performance-critical sections in C or Fortran. Julia was built to be both — as fast as C and as expressive as Python — using LLVM-based just-in-time compilation that turns high-level mathematical code into native machine instructions.

The Julia ecosystem has been shaped substantially by European contributors. Bogumił Kamiński — professor at SGH Warsaw School of Economics in Poland — is the lead developer of DataFrames.jl, the primary data manipulation library in Julia. DataFrames.jl is the Julia equivalent of pandas: it defines how hundreds of thousands of researchers and data scientists interact with tabular data in Julia every day. Kamiński has been the central architect and maintainer of this library since 2016, and his work at a Polish institution demonstrates that Julia's most important infrastructure is actively developed by European researchers.

Kristoffer Carlsson, a software engineer based in Stockholm, Sweden, is among the most prolific contributors to Julia core. He maintains Pkg.jl — Julia's package manager, the tool every Julia user touches on their first day — as well as SparseArrays.jl, LinearAlgebra.jl, and multiple other standard library packages. The package ecosystem that makes Julia usable for production workloads was substantially built by a Swedish engineer.

ETH Zürich is one of the premier Julia research institutions in Europe. Multiple research groups at ETH use Julia as their primary computational language for work in fluid dynamics, machine learning, and computational science. The combination of Julia's performance characteristics and its mathematical expressiveness makes it the natural choice for the kind of differential equations, linear algebra, and optimisation problems that European research universities handle at scale.

For EU research teams, data science organisations, and startups building on scientific computing workloads, deploying Julia to EU infrastructure with sota.io keeps data in Europe and eliminates transatlantic latency.

Why Julia for Production Workloads

Julia's design solves a specific class of problem: computationally intensive work where Python is too slow and C is too painful to write.

Native performance. Julia compiles to native code via LLVM. A Julia function called with specific argument types is compiled once to machine code and subsequent calls execute at full hardware speed — no interpreter overhead, no Python GIL, no overhead from Python's dynamic dispatch. For numerical computing, Julia regularly matches C and Fortran performance on benchmarks that include Monte Carlo simulation, finite element methods, and linear algebra.

Mathematical expressiveness. Julia's syntax reads like mathematics. Variables can be Unicode — you can write θ, , ∂x, and directly in source code. Operator overloading, multiple dispatch, and a type system designed around mathematical abstractions make Julia code that implements algorithms look like the paper it was derived from. This is not cosmetic: it reduces translation errors between mathematical specification and implementation.

Multiple dispatch as a design principle. Julia's core abstraction is multiple dispatch: function behaviour is selected based on the types of all arguments, not just the receiver. This is different from object-oriented dispatch (single dispatch on self) and enables composable, extensible libraries without the adapter pattern. The standard library and most ecosystem packages are written using multiple dispatch, which means user-defined types automatically compose with existing library functions.

First-class parallelism. Julia has native support for multi-threading (@threads), multi-processing (Distributed), GPU computing (CUDA.jl, Metal.jl), and distributed computing (Dagger.jl). A computation written for a single core can be scaled to a multi-core server or a GPU cluster with minimal code changes. For data-intensive backend services, this matters.

Rich numerical ecosystem. The Julia ecosystem for numerical computing is deep: DifferentialEquations.jl (one of the most comprehensive ODE/SDE/PDE solver libraries in any language), Optim.jl, Flux.jl (machine learning), Turing.jl (probabilistic programming), Plots.jl, and DataFrames.jl. These are not ports of Python libraries — they are original Julia implementations that often exceed the performance of their Python equivalents.

Julia Web Backends with Oxygen.jl

For HTTP services and APIs, the primary Julia web framework is Oxygen.jl — a lightweight, Express-style routing library. For teams coming from Python/Flask or Node.js/Express, Oxygen.jl's API is immediately familiar:

# src/server.jl
using Oxygen
using HTTP
using JSON3

@get "/health" function(req::HTTP.Request)
    return Dict("status" => "ok", "language" => "Julia")
end

@get "/compute/{n}" function(req::HTTP.Request, n::Int)
    # Julia compiles this to native code — no Python overhead
    result = sum(sqrt(i) for i in 1:n)
    return Dict("n" => n, "result" => result)
end

@post "/data" function(req::HTTP.Request)
    body = JSON3.read(req.body)
    # Process with DataFrames, statistics, etc.
    return Dict("received" => length(body))
end

# Start the server
serve(host="0.0.0.0", port=8080)

Oxygen.jl handles routing, parameter extraction, and response serialisation. For more complex applications, Genie.jl provides a full MVC framework with database integration, templating, and session management.

Deploying Julia to EU Infrastructure

The fastest path to production on EU infrastructure is a minimal Dockerfile:

FROM julia:1.10-bookworm

WORKDIR /app

# Copy Project.toml and Manifest.toml first for dependency caching
COPY Project.toml Manifest.toml ./

# Install dependencies and precompile
RUN julia --project=. -e "using Pkg; Pkg.instantiate(); Pkg.precompile()"

# Copy source code
COPY src/ ./src/

# Expose port
EXPOSE 8080

CMD ["julia", "--project=.", "src/server.jl"]

The Pkg.precompile() step is important: it compiles all dependencies at build time so the first request does not pay compilation cost. Julia's compilation model means the first call to a function is slower (JIT compilation), but subsequent calls run at native speed. Precompiling in the Dockerfile moves this cost to build time rather than request time.

With sota.io, deploy from the project root:

sota deploy

sota.io detects the Dockerfile, builds the container in EU infrastructure, and deploys to Germany (Frankfurt) by default. The DATABASE_URL environment variable is injected automatically if you provision a PostgreSQL database.

Project Structure for Production Julia APIs

A production Julia web service should separate concerns clearly:

my-julia-api/
├── Project.toml
├── Manifest.toml
├── Dockerfile
├── src/
│   ├── server.jl          # Entry point, route registration
│   ├── routes/
│   │   ├── health.jl
│   │   └── api.jl
│   ├── services/
│   │   ├── database.jl    # DB connection pool
│   │   └── compute.jl     # Heavy computation
│   └── models/
│       └── schemas.jl     # Type definitions
└── test/
    └── runtests.jl

The Project.toml declares dependencies:

[deps]
Oxygen = "21499b5e-2b2d-4e58-b924-09dc1e5d36f0"
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
JSON3 = "0f8b85d8-7e73-4b43-a3f9-60baa3ded5d0"
DataFrames = "a93c6f00-e57d-5684-b466-afe8fa294f15"
LibPQ = "194296ae-ab2e-5f79-8cd4-7183a0a5a0d1"

Install dependencies and generate the lockfile:

julia --project=. -e "using Pkg; Pkg.instantiate()"

Database Access with LibPQ.jl

Julia's primary PostgreSQL client is LibPQ.jl, which provides a direct connection to PostgreSQL without an ORM layer:

# src/services/database.jl
using LibPQ
using DataFrames

function get_connection()
    return LibPQ.Connection(ENV["DATABASE_URL"])
end

function query_users(conn::LibPQ.Connection, limit::Int=100)
    result = execute(conn, """
        SELECT id, email, created_at
        FROM users
        ORDER BY created_at DESC
        LIMIT \$1
    """, [limit])
    return DataFrame(result)
end

function insert_record(conn::LibPQ.Connection, name::String, value::Float64)
    execute(conn, """
        INSERT INTO records (name, value, created_at)
        VALUES (\$1, \$2, NOW())
    """, [name, value])
end

DataFrames.jl integrates directly with LibPQ.jl query results — a DataFrame(result) converts a query result set into a DataFrame with column type inference. This is the natural pattern for data-heavy Julia services: query from PostgreSQL, process with DataFrames, return as JSON.

With sota.io:

# Provision a PostgreSQL database in the same EU data centre
sota postgres create

# DATABASE_URL is injected automatically at runtime

Connection Pooling

For production services handling concurrent requests, use a connection pool rather than creating a new connection per request:

# src/services/database.jl
using LibPQ

const POOL_SIZE = 10
const pool = Channel{LibPQ.Connection}(POOL_SIZE)

function init_pool()
    for _ in 1:POOL_SIZE
        conn = LibPQ.Connection(ENV["DATABASE_URL"])
        put!(pool, conn)
    end
end

function with_connection(f::Function)
    conn = take!(pool)
    try
        return f(conn)
    finally
        put!(pool, conn)
    end
end

Usage in a route handler:

@get "/users" function(req::HTTP.Request)
    with_connection() do conn
        df = query_users(conn)
        return Dict("users" => eachrow(df) |> collect)
    end
end

Julia for Data-Intensive APIs

Julia's performance advantage is most significant for services that do computation on ingress — numerical processing, statistical analysis, optimisation, or machine learning inference:

# Compute a moving average on time series data
function moving_average(data::Vector{Float64}, window::Int)
    n = length(data)
    result = Vector{Float64}(undef, n - window + 1)
    @inbounds for i in 1:(n - window + 1)
        result[i] = sum(data[i:i+window-1]) / window
    end
    return result
end

# This runs at C speed — @inbounds disables bounds checking
# after validation upstream

For EU fintech, healthtech, and research organisations that need to run statistical models or ML inference in the request path, Julia eliminates the Python-performance compromise without requiring a C extension layer.

GDPR and Data Residency for Scientific Computing

EU research institutions and data-driven organisations have strict requirements around data residency. Research data involving human subjects — clinical trials, cohort studies, genomics — must be processed within EU jurisdiction under GDPR and the Data Governance Act.

Julia on EU infrastructure satisfies these requirements at the infrastructure level:

Data in Germany. sota.io deploys to Frankfurt (Germany) by default. All computation happens within EU jurisdiction — no transatlantic data transfers, no Article 46 Standard Contractual Clauses required.

PostgreSQL 17 in the same availability zone. Database and application server co-located in the same EU data centre eliminates cross-border data movement at the persistence layer.

Research data pipelines stay in the EU. A Julia service that ingests sensitive research data, runs analysis, and returns results can be structured so that raw data never leaves the EU — only derived, non-personal results are returned to clients outside the EU.

For ETH Zürich, Max Planck Institute, INED Paris, and other EU research institutions that need to expose Julia-backed computation as a service, this is the correct infrastructure pattern.

Why EU Infrastructure for Julia APIs

Bogumił Kamiński (Poland) built DataFrames.jl. The library that defines how Julia handles tabular data was built at a Polish university. For EU research organisations evaluating open-source supply chain provenance, the core data manipulation library in Julia is maintained by a European researcher at a European institution.

Kristoffer Carlsson (Sweden) maintains Pkg.jl. The package manager — the infrastructure layer that every Julia user depends on — is actively maintained by a Stockholm-based engineer. Julia's package ecosystem exists in its current form because of sustained European contribution.

Latency for EU users. A Julia API deployed in Frankfurt serves German, French, and Dutch users with 5–20ms latency. The same service in us-east-1 adds 80–100ms. For interactive APIs that return computation results, this is perceptible.

EU research alignment. Julia is the language of computational science. The institutions that produce the most Julia users — ETH Zürich, TU Berlin, Université de Paris, Delft University — are European. The infrastructure that runs their services should be in the same jurisdiction as their data and their users.

sota.io deploys Julia applications to Germany (Frankfurt) by default, with PostgreSQL 17 in the same availability zone. No cross-border data transfer, no latency tax, no GDPR complexity.

Getting Started

# Clone or create a Julia project
mkdir my-julia-api && cd my-julia-api
julia --project=. -e "using Pkg; Pkg.add([\"Oxygen\", \"HTTP\", \"JSON3\", \"LibPQ\"])"

# Create your server file
cat > src/server.jl << 'EOF'
using Oxygen, HTTP, JSON3

@get "/health" function(req::HTTP.Request)
    return Dict("status" => "ok")
end

serve(host="0.0.0.0", port=8080)
EOF

# Deploy to EU
sota deploy

Julia compilation, dependency installation, and container build happen on sota.io infrastructure. The first deploy includes full precompilation — subsequent deploys are faster due to layer caching.

The result: a high-performance, GDPR-compliant Julia API running in the EU, built on infrastructure maintained by European engineers.