Tip

Flatpak vs. Snap: Why Snap is still relevant on servers

Snap and Flatpak serve different goals. Explore the architectural differences between them and learn how to package a Spring Boot web server as a snap.

Remember when the Firefox snap was a meme?

Canonical drew ire and criticism in 2021 when it removed Linux's most ubiquitous web browser from the Ubuntu 21.10 repositories. Since then, Firefox has been packaged as a snap as a part of every Ubuntu install. The launch times were horrendous -- upward of 20 seconds. Not Canonical's finest moment.

Nevertheless, the Linux distributor doubled down and later removed Firefox entirely from the standard deb repositories. It continued to invest in the Snap architecture even though Flatpak had matured.

This article explores the differences between Snap and Flatpak, what they were designed to do and why Snap is a better choice in some cases. We'll also walk through how to create and package a Spring web server using Snap.

Flatpak and Snap: Key differences

Snap actually predates Flatpak and evolved alongside Docker. Initially, Snap was designed for server, IoT and mobile applications. Eventually, it added support for desktop apps, but it was never built solely for that purpose.

Decentralized vs. bundled

Flatpak relies on shared runtimes to reduce app size and startup time. Snap packages an application together with all its dependencies and libraries into a single self-contained bundle, similar to macOS. Those shared runtimes made Flatpak the preferred choice for many Linux desktop distributions -- they launched faster, felt more modular and played nicely within a decentralized Linux ecosystem.

However, Flatpak's shared runtimes are often bulky -- frequently several gigabytes each. Since multiple applications require slightly different versions of the same runtime, users often encounter significant disk space bloat.

Whereas Flatpak's architecture was made friendlier for desktop Linux, Snap was designed from the ground up for a broader scope -- to provide predictable, reproducible environments across servers, edge devices and embedded systems.

Snap also tightly integrates with low-level services such as systdemd, enabling it to define low-level system services, daemons and sockets in ways Flatpak simply cannot.

As a result, entire server stacks such as LXD, MicroK8s or complete Spring Boot web servers can be installed as a Snap. In fact, an entire OS can be packaged into a Snap.

Snap's containerization architecture means that any version of a Snap can be installed onto any Snap-capable server and can integrate tightly with the system as a drop-in replacement for .deb packages. This makes version management trivial. Often, installing the snap version of a package is far simpler than finding a suitable PPA, and requires much less overhead and configuration than Docker containers do.

With Snap, rollbacks are easy too. It's a single command: sudo snap revert openjdk. Snap makes version management trivial.

How to package your own snap

To demonstrate Snap, we'll create a simple Spring web server and package it within a snap. Follow these steps:

1. Prerequisites

To get started with Snap, first install Maven and the Snapcraft CLI build tool, and set up the directory.

sudo apt update

sudo apt install -y openjdk-17-jdk maven

sudo snap install snapcraft --classic

mkdir spring-snap && cd spring-snap

2. Create pom.xml

Then, set up the project with properties, dependencies and plugins:

<project xmlns="http://maven.apache.org/POM/4.0.0"

         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0

                             https://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>

 

  <parent>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-parent</artifactId>

    <version>3.2.5</version>

  </parent>

 

  <groupId>com.example</groupId>

  <artifactId>springweb</artifactId>

  <version>1.0.0</version>

  <properties><java.version>17</java.version></properties>

 

  <dependencies>

    <dependency>

      <groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-web</artifactId>

    </dependency>

  </dependencies>

 

  <build>

    <plugins>

      <plugin>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-maven-plugin</artifactId>

      </plugin>

    </plugins>

  </build>

</project>

3. Create a Java web controller

Next, create a Java web controller in the folder src/main/java/com/example/springweb/Application.java:

package com.example.springweb;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication

@RestController

public class Application {

  public static void main(String[] args) {

    SpringApplication.run(Application.class, args);

  }

 

  @GetMapping("/")

  public String hello() {

    return "Hello from Spring Boot in a Snap!";

  }

}

4. Build a jar

mvn -q package spring-boot:repackage

cp target/springweb-1.0.0.jar app.jar

5. Create a run.sh wrapper in your project root

Now create a rusn.sh wrapper in the project root to launch the server. This will be the entry point for the snap.

#!/bin/sh

exec /usr/bin/env java -jar "$SNAP/app.jar"

chmod 755 run.sh

6. Create snapcraft.yaml in project root

Next, create a snapcraft.yaml file in the project root.

name: springweb

base: core22

version: '1.0'

summary: Spring Boot web server in a Snap

description: Serves http://localhost:8080 with a greeting.

 

confinement: classic

grade: stable

 

apps:

  springweb:

    command: run.sh

 

parts:

  app:

    plugin: dump

    source: .

7. Build, install and run the Snap

Now it's time to build the Snap:

snapcraft --destructive-mode

Then run it:

sudo snap install springweb_1.0_*.snap --classic --dangerous

springweb &

SERVER_PID=$!

8. Confirm using localhost in a browser

Finally, fire up a web browser and access http://localhost:8080. You should see "Hello from Spring Boot in a Snap!" once the server has been built and launched.

The case for Snap

While Snap may have gotten off to a rocky start in the desktop world, it's quietly become a powerhouse in areas where Flatpak was never intended to shine. It excels when excellent system integration, IoT and server-side applications are required.

Whether you're deploying Java microservices, setting up server environments or managing a fleet of Raspberry Pis, Snap excels where system integration is needed. It's not about Snap vs. Flatpak, it's about finding the right tool for the job.

And as you've just seen, creating your own Snap can be as simple as a few files in a folder. You don't need to rewrite your application, build a Docker container or mess with arcane distro packaging rules. Just drop in your files, define a manifest, and you're done.

Canonical may have turned Firefox into a meme in 2021, but Snap itself? It has matured and is more relevant now than ever.

Joseph B. Ottinger has held senior roles in software engineering and project management. He's written countless articles and multiple books on various languages, architectures and implementations, including Hibernate and Spring.

Dig Deeper on Application management tools and practices