Skip to content

ScalaHeaven/play-devcontainer

Repository files navigation

Play Devcontainer

This repository is a ready-to-open Scala 3 and Play Framework workspace for VS Code Dev Containers. It gives newcomers the same local environment every time: a JDK, Scala tools, sbt, Metals editor support, formatting, debugging, tests, and a production Docker image path.

Quick Start

Open the folder in VS Code and run Dev Containers: Reopen in Container.

Inside the container:

scala3-compiler -version
sbt -Dsbt.batch=true run

Then visit:

http://localhost:9000/

Try the JSON endpoints:

curl http://localhost:9000/api/health
curl http://localhost:9000/api/hello/Scala

Build and run the application image:

docker build -t play-devcontainer .
docker run --rm -p 9000:9000 play-devcontainer

For real deployments, set a strong secret instead of using the demo fallback:

docker run --rm -p 9000:9000 \
  -e PLAY_HTTP_SECRET_KEY="$(openssl rand -hex 32)" \
  play-devcontainer

Mental Model

Scala development in this repository has three layers:

  1. The container provides the operating system, JDK, Node.js, Codex, Coursier, Scala CLI, sbt, and Metals MCP.
  2. The Play project provides controllers, routes, configuration, sbt build definition, dependency resolution, packaging, formatting, and tests.
  3. VS Code and Metals provide editor features such as diagnostics, go-to definition, completion, build import, and debugging.

The editor does not compile Scala by itself. VS Code talks to Metals, Metals talks to a build server through BSP, and the build server uses sbt to compile and understand the project.

Core Scala Components

JDK

Scala runs on the JVM, so the Java Development Kit is the foundation of the environment. The devcontainer uses Eclipse Temurin JDK 26. The production image uses a JDK while building and a smaller JRE while running.

The devcontainer also installs JDK source files into src.zip so editor navigation can jump into Java standard library classes.

Scala 3

build.sbt sets:

ThisBuild / scalaVersion := "3.8.3"

That version is used by sbt for the main project. The container also installs scala3-compiler so you can check the compiler directly.

Play Framework

This project uses Play Framework 3.0.10, the latest stable Play 3 release at the time this starter was converted. The Play sbt plugin adds the standard Play layout and tasks:

  • app/: Scala controllers and Twirl views
  • conf/routes: HTTP route definitions
  • conf/application.conf: application configuration
  • test/: Play controller and route tests
  • sbt run: starts the development server on port 9000
  • sbt stage: builds a runnable production layout under target/universal/stage

The starter includes:

  • GET /: a simple HTML welcome page
  • GET /api/health: JSON health check
  • GET /api/hello/:name: JSON greeting endpoint

sbt

sbt is the main build tool for this repository. It is responsible for:

  • reading build.sbt
  • reading project/build.properties
  • downloading Scala, Play, plugins, and library dependencies
  • compiling routes, Twirl templates, and Scala code
  • running the Play development server with sbt run
  • running tests with sbt test
  • building the production stage with sbt stage
  • checking and applying formatting with sbt scalafmtCheckAll and sbt scalafmtAll
  • exposing project metadata to Metals through BSP

The project pins sbt in project/build.properties:

sbt.version=1.12.11

The devcontainer creates /usr/local/bin/sbt as a small Coursier-based wrapper instead of storing a launcher script in the repository.

Coursier And Scala CLI

Coursier is the dependency and tool installer used by this setup. It downloads Scala tools and JVM artifacts into the Coursier cache, normally under:

/home/vscode/.cache/coursier

The devcontainer uses Coursier to install:

  • scala3-compiler
  • scala-cli
  • metals-mcp
  • the sbt launcher used by the sbt wrapper

Scala CLI is available for scripts and experiments, but sbt is the project build tool for this Play application.

Metals, BSP, Scalafmt, And SemanticDB

Metals is the Scala language server. It powers diagnostics, completion, go-to definition, symbol search, code actions, build import, and debug integration in VS Code.

BSP means Build Server Protocol. It is the bridge between Metals and sbt. Generated .bsp files are ignored by git and should not be edited by hand.

.scalafmt.conf configures Scalafmt 3.10.7 with the Scala 3 dialect:

sbt -Dsbt.batch=true scalafmtCheckAll
sbt -Dsbt.batch=true scalafmtAll

build.sbt enables SemanticDB so Metals and other tools can use richer semantic information.

Project Files

  • build.sbt: pins Scala 3.8.3, enables SemanticDB, enables PlayScala, and declares Play/test dependencies.
  • project/build.properties: pins sbt 1.12.11.
  • project/plugins.sbt: declares the Play sbt plugin and sbt-scalafmt.
  • app/controllers/HomeController.scala: example Play controller.
  • app/views/index.scala.html: HTML welcome page.
  • conf/routes: route table for the HTML page and JSON endpoints.
  • conf/application.conf: Play application configuration.
  • test/controllers/HomeControllerSpec.scala: controller and route tests.
  • .vscode/launch.json: Metals/Scala debug launch config for Play.
  • Dockerfile: production multi-stage build that runs sbt stage.

Devcontainer Files

.devcontainer/devcontainer.json

This is the entry point for VS Code Dev Containers. It defines the devcontainer name, development Dockerfile, remote user, mounted host directories, VS Code extensions, and lifecycle commands.

.devcontainer/Dockerfile

This builds the interactive development environment. It installs Eclipse Temurin JDK 26, basic OS tools, Node.js 26, Codex, a non-root vscode user, JDK sources, Coursier, Scala compiler, Scala CLI, Metals MCP, and an sbt wrapper.

.devcontainer/post-start.sh

This script repairs permissions, configures Git, syncs selected host SSH/Codex config, configures Codex Metals MCP, and starts metals-mcp on port 8421.

Production Dockerfile

The repository root Dockerfile is separate from the devcontainer Dockerfile. It builds a runnable Play application image, not an editor environment.

It has two stages:

  1. build: installs Coursier and sbt, copies build metadata, downloads dependencies, copies Play sources/config/tests, and runs sbt stage.
  2. runtime: starts from a smaller JRE image, copies target/universal/stage, exposes port 9000, and runs the generated Play launcher.

The image uses these runtime settings:

  • PORT: defaults to 9000
  • PLAY_HTTP_SECRET_KEY: defaults to a long local-demo-only value

The fallback secret is only for local demos. Set PLAY_HTTP_SECRET_KEY in any real environment.

Common Commands

Run sbt commands serially. Starting multiple sbt processes at the same time can hit the sbt boot socket lock and fail with ServerAlreadyBootingException.

# Start the Play development server
sbt -Dsbt.batch=true run

# Compile
sbt -Dsbt.batch=true compile

# Run tests
sbt -Dsbt.batch=true test

# Check formatting
sbt -Dsbt.batch=true scalafmtCheckAll

# Build the production stage
sbt -Dsbt.batch=true stage

# Build and run the production image
docker build -t play-devcontainer .
docker run --rm -p 9000:9000 play-devcontainer

# Check installed tool versions
java -version
scala3-compiler -version
scala-cli --version
sbt sbtVersion

Generated And Ignored Directories

The following directories are generated by tools and should normally not be edited manually:

  • .bsp/: build server connection files
  • .metals/: Metals workspace data and logs
  • .scala-build/: Scala CLI build data
  • target/: sbt and compiler output
  • project/target/: sbt build-project output
  • project/project/: nested sbt build metadata

They are ignored by .gitignore. .dockerignore also excludes them from Docker build context so images do not include local build caches or editor state.

Troubleshooting

If Metals does not import the build, run:

sbt -Dsbt.batch=true compile

Then use the VS Code command Metals: Import build.

If generated directories have permission problems, restart the devcontainer. The startup script repairs ownership and write permissions for the common build directories.

If Codex cannot reach Metals MCP, check:

cat .metals/mcp/metals-mcp.log

The default MCP endpoint is:

http://127.0.0.1:8421/mcp

About

a devcontainer to work with Play Framework

Topics

Resources

Stars

Watchers

Forks

Contributors