5 min read
Shipping Scala to Production
Prerequisites: Building web services
Packaging
// illustrative
// project/plugins.sbt
addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.11.1")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.1")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.4")
// build.sbt
enablePlugins(JavaAppPackaging, DockerPlugin)
dockerBaseImage := "eclipse-temurin:21-jre"
dockerExposedPorts := Seq(8080)
| Command | Produces |
|---|---|
sbt assembly |
one "fat" JAR with all dependencies |
sbt stage |
a folder with start scripts and JARs |
sbt Docker/publishLocal |
a Docker image |
scala package . --assembly |
a runnable JAR with Scala CLI |
Continuous integration
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with: { distribution: temurin, java-version: 21, cache: sbt }
- uses: sbt/setup-sbt@v1
- run: sbt scalafmtCheckAll compile test
Configuration and operations
- Configuration — read settings from environment variables with defaults (pureconfig, ciris), and fail fast at startup on bad config.
- JVM settings — set heap limits relative to the container
(
-XX:MaxRAMPercentage=75) and choose a garbage collector (G1 by default; ZGC for low latency). - Observability — structured JSON logs, metrics, and distributed tracing via OpenTelemetry.
- Health checks — liveness and readiness endpoints for Kubernetes.
Keeping a codebase healthy
- Compile with strict warnings (
-Wunused:all,-Werrorin CI) and use scalafmt and Scalafix. - Keep dependencies current with Scala Steward, which opens update pull requests automatically.
- Model the domain with case classes, enums, and opaque types; keep side effects at the edges; test the pure core thoroughly.
- Pick one style for the team — "direct style" Scala, Cats Effect, or ZIO — and apply it consistently.
What's next
Try the Advanced Projects, then read What You Should Know Now — and What's Next for where to go from here.