Blackjack
A multiplayer blackjack game where friends sit at a public or private table and play rounds against the dealer. The Spring Boot server deals the cards and enforces the rules, and every screen at the table updates over a WebSocket the moment anything changes.
- Status
- Ongoing
- Stack
- Java 21, Spring Boot, WebSocket, Vue 3, Vite, Caddy, Docker
- Links
- Demo ↗



Why I built it
It started as a simple card game assignment that I ended up enjoying more than I expected. I decided to revisit it in my free time, take it a few steps further, and turn it into a fully playable Blackjack game. Eventually, I thought it would be fun to make it multiplayer too, so I could play with friends. What started as a small assignment turned into a fun side project to experiment with and build upon.
How it works
# -------- Build stage --------
FROM eclipse-temurin:21-jdk AS build
WORKDIR /app
# Wrapper and build scripts first, so dependencies are cached between builds
COPY gradlew settings.gradle build.gradle ./
COPY gradle ./gradle
RUN ./gradlew --no-daemon dependencies > /dev/null
COPY src ./src
RUN ./gradlew --no-daemon bootJar -x test
# -------- Runtime stage --------
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
RUN addgroup -S app && adduser -S app -G app
COPY --from=build /app/build/libs/*.jar app.jar
USER app
ENV JAVA_OPTS="-XX:MaxRAMPercentage=75"
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "exec java $JAVA_OPTS -jar app.jar"]services:
web:
build:
context: .
dockerfile: web/Dockerfile
ports:
- "80:80"
- "443:443"
- "443:443/udp"
environment:
SITE_ADDRESS: ${SITE_ADDRESS:-localhost}
volumes:
- caddy_data:/data
- caddy_config:/config
depends_on:
server:
condition: service_healthy
restart: unless-stopped
server:
build: server
environment:
JAVA_OPTS: "-XX:MaxRAMPercentage=75"
# Optional overrides of blackjack.* settings, e.g. for shorter test runs
BLACKJACK_AWAY_GRACE: ${BLACKJACK_AWAY_GRACE:-2m}
BLACKJACK_HOST_AWAY: ${BLACKJACK_HOST_AWAY:-30s}
mem_limit: 512m
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/actuator/health"]
interval: 10s
timeout: 3s
retries: 3
start_period: 30s
restart: unless-stopped
volumes:
caddy_data:
caddy_config: