LAMPRISM

Back to News
Blog

Lampray Technical Deep Dive: Architecture, Design, and Getting Started

2026-05-23 RollW 6 min

Lampray is an open-source blog platform built from the ground up with Spring Boot 3 on the backend and Vue 3 on the frontend. It's designed for creators, developers, and communities who want full control over their publishing infrastructure without sacrificing modern development practices.

In this post, we'll take a deep dive into Lampray's technical architecture, explore the design decisions behind it, and walk through how to get it running in your own environment.

Architecture Overview

Lampray follows a modular monolith architecture. Rather than jumping straight into microservices, the project is organized as a multi-module Gradle project where each module has a clear, bounded responsibility. This keeps deployment simple while maintaining clean separation of concerns.

lampray/
├── lampray-common/          # Shared utilities and base abstractions
├── lampray-common-data/     # Data layer: entities, repositories, migrations
├── lampray-content/         # Content management: articles, pages, taxonomies
├── lampray-file/            # File storage abstraction (local, S3, etc.)
├── lampray-iam/             # Identity and access management
├── lampray-push/            # Notification and push services
├── lampray-system/          # System configuration and monitoring
├── lampray-user/            # User profile and account management
├── lampray-web/             # Web entry point, REST controllers, security config
├── lampray-frontend/        # Vue 3 single-page application

Each module is a Gradle subproject with its own build.gradle.kts. The lampray-web module acts as the application entry point, pulling in dependencies from all other modules and exposing RESTful APIs consumed by the Vue frontend.

Why a Modular Monolith?

The modular monolith approach gives us the best of both worlds:

  • Single deployable artifact — One JAR file to build, ship, and run. No orchestration overhead.
  • Clear module boundaries — Each module has a well-defined API surface. Modules communicate through interfaces, not implementation leakage.
  • Future-proof — When a module grows large enough, extracting it into a standalone microservice is straightforward because the boundaries already exist.

Technology Stack

Backend

ComponentTechnology
FrameworkSpring Boot 3
LanguageJava 17, Kotlin
Build SystemGradle (Kotlin DSL)
SecuritySpring Security
DatabaseSQLite, H2, MySQL, PostgreSQL, MariaDB, Oracle, SQL Server
ORMSpring Data JPA / Hibernate

Frontend

ComponentTechnology
FrameworkVue 3 (Composition API)
UI LibraryNaive UI
LanguageTypeScript
BuildVite

Multi-Database Strategy

One of Lampray's standout features is its support for seven database engines out of the box. This was not an afterthought — it was a deliberate architectural decision from day one.

How It Works

Lampray uses Spring Data JPA with Hibernate as the ORM layer. Database dialect selection is automatic based on the configured type field in the TOML configuration file. The application supports three target formats for maximum flexibility:

  • Network addresshost:port syntax (e.g., localhost:3306 for MySQL)
  • File-basedfile:./data/app.db or file:/absolute/path/to/db
  • In-memorymemory (useful for development and testing)

Here's a MySQL configuration example:

[database]
type = "mysql"
target = "localhost:3306"
name = "lampray"
username = "root"
password = "password"
options = ["useSSL=false", "serverTimezone=UTC"]

And a production-ready SQLite file-based configuration:

[database]
type = "sqlite"
target = "file:./data/lampray.db"
name = "lampray"
username = ""
password = ""

Dialect Detection

The type field maps to a DatabaseType enum that resolves the appropriate Hibernate dialect, JDBC driver, and connection strategy. This abstraction lives in lampray-common-data and is one of the earliest modules designed in the project.

public enum DatabaseType {
    SQLITE,
    H2,
    MYSQL,
    POSTGRESQL,
    MARIADB,
    ORACLE,
    SQLSERVER;
    
    public String dialect() {
        return switch (this) {
            case SQLITE -> "org.hibernate.dialect.SQLiteDialect";
            case H2 -> "org.hibernate.dialect.H2Dialect";
            case MYSQL -> "org.hibernate.dialect.MySQLDialect";
            // ...
        };
    }
}

Schema Initialization

When the application starts for the first time, Lampray automatically creates all required tables and indexes. This is powered by Hibernate's ddl-auto feature combined with Flyway-style migration scripts for production-safe schema evolution.

Build Pipeline and Distribution

Lampray's Gradle build pipeline is designed to produce three artifact types depending on the deployment target.

Standard JAR

./gradlew build

This produces an executable JAR in lampray-web/build/libs/. It includes an embedded Tomcat server and all dependencies.

Distribution Package

./gradlew package

This creates a distribution tarball (lampray-{version}-dist.tar.gz) that includes:

  • The base JAR file
  • Startup scripts (bin/lampray start|stop|status)
  • Default configuration templates
  • A conf/ directory for externalized configuration

The startup script handles JVM options, classpath construction, and PID file management — making it suitable for production environments without additional tooling.

JAVA_OPTS="-Xmx1024m -Xms64m" bin/lampray start

Docker Image

./gradlew buildImage

This produces a Docker image tagged lampray:{version} using the project's Containerfile. The image is optimized for small size and fast startup.

docker run \
  -d \
  -p 5100:5100 \
  -p 5101:5101 \
  --network lampray \
  -v /path/to/conf:/app/lampray/conf \
  --name lampray lampray:{version}

Configuration Architecture

Lampray uses TOML for its configuration file format. TOML was chosen over YAML and JSON for its clarity and strict typing — it's harder to make ambiguous configuration errors.

The configuration system supports:

  • Automatic file resolution — Looks for lampray.toml in the current directory, then falls back to conf/lampray.toml relative to the working directory.
  • Command-line override — Use --config or -c to specify an explicit path.
  • Environment variable passthroughJAVA_OPTS is respected for JVM-level tuning.
bin/lampray start --config /etc/lampray/production.toml

Security Architecture

Lampray uses Spring Security as its authentication and authorization framework. The lampray-iam module handles:

  • User registration and authentication — Session-based auth with JWT support for API access
  • Role-based access control — Roles map to granular permissions on content operations
  • Password hashing — BCrypt with configurable strength
  • Session management — Concurrent session control and timeout configuration

The security configuration in lampray-web wires everything together declaratively:

@Configuration
@EnableWebSecurity
class SecurityConfig(
    private val userDetailsService: UserDetailsService
) {
    @Bean
    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
        http
            .authorizeHttpRequests {
                it.requestMatchers("/api/public/**").permitAll()
                    .requestMatchers("/api/admin/**").hasRole("ADMIN")
                    .anyRequest().authenticated()
            }
            .formLogin { /* ... */ }
            .logout { /* ... */ }
        return http.build()
    }
}

Content Management System

The content module (lampray-content) is the heart of the platform. It provides:

  • Article CRUD — Create, read, update, and delete articles with markdown rendering
  • Tagging and categorization — Flexible taxonomy system for organizing content
  • Pagination and search — Server-side pagination with full-text search support
  • Media attachments — File upload integrated with lampray-file for storage abstraction

Articles are stored with both raw markdown and rendered HTML, allowing the API to serve either format depending on the consuming client.

Frontend Architecture

The Vue 3 frontend (lampray-frontend) is built with the Composition API and Naive UI component library. Key architectural decisions include:

  • Pinia for state management — Lightweight, TypeScript-native store pattern
  • Vue Router — Client-side routing with lazy-loaded route components
  • Axios HTTP client — Centralized API client with interceptors for auth token management
  • Vite — Fast HMR during development, optimized production builds

The frontend communicates with the backend through a RESTful API designed with consistent resource naming and standardized error responses.

Getting Started

Ready to try Lampray? Here's the quickest path from zero to running:

Prerequisites

  • Java 17+
  • Gradle (or use the included Gradle wrapper)

Clone and Build

git clone https://github.com/roll-w/lampray.git
cd lampray
./gradlew assemble -x test

Configure

Create a lampray.toml file in the project root:

[database]
type = "sqlite"
target = "file:./data/lampray.db"
name = "lampray"
username = ""
password = ""

Run

java -jar lampray-web/build/libs/lampray-web-*.jar start

The application starts on port 5100 by default. Open http://localhost:5100 in your browser.

What's Next

Lampray is under active development. The current roadmap includes:

  • Plugin system — Extend functionality through a modular plugin API
  • Multi-tenancy — Run multiple sites from a single instance
  • Webhook support — Integrate with external services through event-driven webhooks
  • Rich text editor — In-browser WYSIWYG editing alongside markdown
  • Performance optimizations — Caching layer, CDN integration, and query optimization

The project is fully open source under the Apache License 2.0. Contributions, bug reports, and feature requests are welcome on GitHub.