---
title: "Fly With Alpine"
description: "Explore Alpine as an alternative to Debian based Linux distributions"
---

# Fly With Alpine

![A drone delivering a printer](https://fly.io/blog/fly-with-alpine/assets/fly-with-alpine-cover.webp)

[Annie Ruygt](https://annieruygtillustration.com/)

Reduce image sizes and improve startup times by switching your base image to Alpine Linux.

Before proceeding, a caution. This is an engineering trade-off. Test carefully before deploying to production.

By the end of this blog post you should have the information you need to make an informed decision.

## Introduction

[Alpine Linux](https://www.alpinelinux.org/about/) is a Linux distribution that advertises itself as Small. Simple. Secure.

It is indisputably smaller than the alternatives – when measured by image size. More on that in a bit. Some claim that this results in less memory usage and better performance. Others dispute these claims. For these, it is best that you test the results for yourself with your application.

Simple is harder to measure. Some of the larger differences, like [OpenRC](https://github.com/OpenRC/openrc#readme) vs [SystemD](https://systemd.io/), are less relevant in container environments. Others, like [BusyBox](https://busybox.net/) are implementation details. Essentially what you get is a Linux distribution with perhaps a number of standard packages (e.g., bash) not installed by default, but these can be easily added if needed.

Secure is definitely an important attribute. The alternatives make comparable claims in this area. Do your own research in this area and come to your own conclusions.

Not mentioned is the downside: Alpine Linux has a smaller ecosystem that the alternatives, particularly when compared to Debian.

## Baseline

Let’s start with a baseline consisting of the Dockerfiles produced by `fly launch` for some of the most popular frameworks:

```
FROM fideloper/fly-laravel:${PHP_VERSION}
FROM hexpm/elixir:1.12.3-erlang-24.1.4-debian-bullseye-20210902-slim
FROM node:${NODE_VERSION}-slim
FROM oven/bun:${BUN_VERSION}-slim
FROM python:${PYTHON_VERSION}-slim-bullseye
FROM ruby:$RUBY_VERSION-slim
```

What may not be obvious to the naked eye from these results is that the base image for these is one of the following:

- Debian Bookworm (the current “stable” distribution)
- Debian Bullseye (the previous “stable” distribution)
- Ubuntu Focal Fossa (the previous LTS release of Ubuntu)

Once you factor in that Ubuntu is based on Debian, the conclusion is that Debian is effectively the default distribution for fly IO. Rest assured that this isn’t the result of a devious conspiracy by Fly.io, but rather a reflection of the default choices made independently by the developers of a number of frameworks and runtimes. Beyond this, all Fly.io is doing is choosing the “slim” version of the default distribution for each framework as the base.

What’s likely going on here is a virtuos circle: people choose Debian because of the ecosystem, and ecosystem grows because people chose Debian.

Now lets compare base image sizes:

|  | Alpine | Debian slim |
| --- | --- | --- |
| Bun 1.0.18 | 43.10M | 63.84M |
| Node 21.4.0 | 46.83M | 70.08M |
| Python 3.12.1 | 17.59M | 45.36M |
| Ruby 3.2 | 40.14M | 74.36M |

And these numbers are just the for the base images. I’ve measured a minimal Rails/Postgresql/esbuild application at 304MB on Alpine and 428MB on Debian Slim. A minimal Bun application at 110MB on Alpine and 173MB on Debian Slim. And a minimal Node application at 142MB on Alpine and 207MB on Debian Slim.

In each case, corresponding Alpine images are consistently smaller than their Debian slim equivalent.

## Switching Distributions

Switch distributions (and switching back!) is easy.

The first change is to replace `-slim` with `-alpine` in `FROM` statements in your `Dockerfile`.

Next is to replace `apt-get update` with `apk update` and `apt-get install` with `apk add`. Delete any options you may have like `-y` and `--no-install-recommends` - they aren’t needed.

Now review the names of the packages you are installing. Many are named the same. A few are different. You can use [alpine packages](https://pkgs.alpinelinux.org/packages) to look for ones to use. Some examples of differences:

| Debian | Alpine |
| --- | --- |
| build-essential | build-base |
| chromium-sandbox | chromium-chromedriver |
| default-libmysqlclient-dev | mysql-client |
| default-mysqlclient | mysql-client |
| freedts-bin | freedts |
| libicu-dev | icu-dev |
| libjemalloc | jemalloc-dev |
| libjpeg-dev | jpeg-dev |
| libmagickwand-dev | imagemagick-libs |
| libsqlite3-0 | sqlite-dev |
| libtiff-dev | tiff-dev |
| libvips | vips-dev |
| node-gyp | gyp |
| pkg-config | pkgconfig |
| python | python3 |
| python-is-python3 | python3 |
| sqlite3 | sqlite |

Note: the above is just an approximation. For example, while `libsqlite3-0` and `sqlite-dev` include everything you need to build an application that uses sqlite3, all that is needed at runtime is `sqlite-lib`. This relentless attention to detail contributes to smaller final image sizes.

Note: For Bun, Node, and Rails users, knowledge of how to apply the above changes are included in recent versions of the dockerfile generators that we provide. After all, computers are good at `if` statements:

```
bunx dockerfile --alpine
npx dockerfile --alpine
bin/rails generate dockerfile --alpine
```

## Choose your own Linux Distribution

Deploy your project in a few minutes with Fly Launch. Then do more with Fly Machines.

[Run your entire stack near your users](https://docs.fly.io)

## Potential issues

Over time, we’ve noted a number of issues.

- Alpine uses [musl](https://musl.libc.org/) for a runtime library. Debian uses [glibc](https://www.gnu.org/software/libc/). Software tested on glibc may not work as expected on musl. And there are other potential compatibility issues like [DNS](https://bell-sw.com/blog/how-to-deal-with-alpine-dns-issues/).
- Debian includes both `adduser` and `useradd`. Alpine, by default, only includes `adduser`. This can be addressed by installing package like [shadow](https://pkgs.alpinelinux.org/package/edge/community/armv7/shadow), or switching to `adduser`.
- Packages like [node-build](https://github.com/nodenv/node-build) require `bash` which isn’t included by default. Adding it back in allows `node-build` to run to completion, but the end result is that a precompiled Debian executable is installed that won’t run on Alpine. An alternative is to download an [unofficial build](https://unofficial-builds.nodejs.org/).
- Release candidates for Alpine may not get the same level of testing as Debian resulting in problems like [sqlite3-ruby not working on Alpine 3.19](https://github.com/sparklemotion/sqlite3-ruby/issues/434). In cases like this, stay back on previous versions of Alpine for a short while, or compile the gem for yourself. These issues are temporary.
- Some packages, like Chrome, are not available for Alpine. Alternatives like Chromium may be necessary.

## Conclusion

While not as large a community as Debian, there is a substantial number of happy Alpine users.

For the forseeable future, the default for both frameworks and there fly.io will remain Debian, but we make it easy to switch.

Try it out! Hopefully this blog has provided insight into what you should evaluate for before you switch.
