← Back to list

Kivy (Python): Linux distribution (Linux, Snap, Flatpak, AppImage)

Kivy runs on Windows, macOS, Linux, iOS, Android. This article summarizes how to distribute apps in the most common Linux package formats.

Viachaslau Lyskouski · 2025-09-28 19:04 · 13 claps · 7.7 min read paywalled
#kivy #python #kivy-python #continuous-integration #continuous-delivery
Open on Medium ↗
Wiki topics: 🔓 · Open Source 🥊 · Combat Sports

Kivy (Python): Linux distribution (Linux, Snap, Flatpak, AppImage)

Image from https://kivy.org/doc/stable/gettingstarted/intro.html

Image from https://kivy.org/doc/stable/gettingstarted/intro.html

Preamble Kivy is an open-source, cross-platform framework that enables the development of modern applications from a single codebase (see the demo project). Built on a modular architecture and an event-driven programming model, it allows developers to design responsive and interactive user interfaces with ease. Powered by Python — a high-level, dynamically typed, and interpreted language — Kivy benefits from Python’s readability, simplicity, and extensive ecosystem of libraries, making it an excellent choice for rapid development and seamless integration with a wide range of technologies.

Objectives Kivy is running smoothly across Windows, macOS, Linux, iOS, Android. In addition to simplifying cross-platform development, Kivy offers flexibility in distribution: applications can be packaged as native executables for desktops, bundled as mobile apps with tools like Buildozer, or deployed on embedded systems such as Raspberry Pi.

Overview In this article, we will primarily focus on the Linux distribution process of the application https://github.com/lyskouski/app-language, demonstrating how to package a Kivy application into an executable formats: Linux, Snap, Flatpak, and AppImage.

For distribution, we need to prepare a configuration file, that will be taken by PyInstaller to build the solution:

# -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.hooks import collect_dynamic_libs

# Force ffpyplayer backend on Linux
import os
os.environ.setdefault("KIVY_AUDIO", "ffpyplayer")

a = Analysis(
    ['../src/main.py'],
    pathex=['.'],
    binaries=collect_dynamic_libs('kivy'),
    datas=[
        ("../src/component/*.py","component"),
        ("../src/controller/*.py","controller"),
        ("../src/l18n/*.py","l18n"),
        ("../src/template/*.kv","template"),
    ],
    hiddenimports=[
        'kivy.core.window.window_sdl2',
        'kivy.core.window.window_egl_rpi',
        'kivy.core.image.img_pil',
        'kivy.core.audio.audio_sdl2'
    ],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    noarchive=False,
    optimize=0,
)
pyz = PYZ(a.pure)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    [],
    exclude_binaries=True,
    name='tlum',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    console=False,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
    icon=['logo.png'],
)
coll = COLLECT(
    exe,
    a.binaries,
    a.datas,
    strip=False,
    upx=True,
    upx_exclude=[],
    name='tlum',
)
  • collect_dynamic_libs('kivy'): Ensures PyInstaller includes Kivy’s required shared libraries (e.g., SDL2, GLEW, etc.), which may not be detected automatically.
  • Environment variable KIVY_AUDIO=ffpyplayer: Forces the use of the ffpyplayer backend on Linux (important since sdl2 audio often freezes on some Linux distributions).
  • Entry point ../src/main.py — our main application file.
  • hiddenimports — ensures that certain Kivy backends (windowing, image handling, audio) are packaged, since PyInstaller’s dependency analysis can miss them.

Solution Basic steps for each of the solution would be to make a checkout, install python and project dependencies:

jobs:
  build:
    name: Create Linux Build
    # Error: version `GLIBC_2.38' not found
    # Build Linux app in the oldest Ubuntu for better compatibility
    runs-on: ubuntu-22.04

    steps:
      - uses: actions/checkout@v4

      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.12'

      - name: Install requirements
        shell: bash
        run: |
          pip install --upgrade pip
          pip install -r requirements.txt

Linux package As a first step, we need to install all relevant to the project packages (install the SDL2 and SDL2-mixer libraries, required for Kivy’s graphics and audio backends, FFmpeg and related multimedia libraries [libav], needed because our Kivy app uses ffpyplayer [audio/video backend]*). Without this step, the PyInstaller-built app would fail at runtime due to missing native libraries.

      - name: Install system dependencies for Kivy
        shell: bash
        run: |
          sudo apt-get update
          sudo apt-get install libsdl2-2.0-0 libsdl2-dev \
              libsdl2-mixer-2.0-0 libsdl2-mixer-dev \
              ffmpeg libavdevice-dev libavformat-dev \
              libavfilter-dev libswscale-dev

      - name: Build executable
        shell: bash
        run: |
          cd linux
          export KIVY_WINDOW=sdl2
          export SDL_VIDEODRIVER=dummy
          export KIVY_GL_BACKEND=angle_sdl2
          export KIVY_LOG_LEVEL=warning
          python -m PyInstaller tlum.spec
          mkdir -p ./dist/tlum/assets
          cp -r ./../assets/* ./dist/tlum/assets/

      - name: Compress Linux Package
        run: tar -czf "$GITHUB_WORKSPACE/tlum_Linux.tar.gz" .
        working-directory: linux/dist/tlum

Environment variables (needed specifically for GitHub pipeline):

  • KIVY_WINDOW=sdl2 → force Kivy to use the SDL2 window provider.
  • SDL_VIDEODRIVER=dummy → disables actual window creation (important in CI, since GitHub runners don’t have a graphical display).
  • KIVY_GL_BACKEND=angle_sdl2 → sets OpenGL backend to ANGLE (ensures compatibility with PyInstaller packaging).
  • KIVY_LOG_LEVEL=warning → suppresses noisy [TRACE] logs.

That’s it, now we have a package that can be distributed via https://www.appimagehub.com.

Snap package A snap is essentially a bundled package containing one or more applications along with all their dependencies. What’s remarkable about snaps is their ability to run consistently across a wide array of Linux distributions, without requiring any modifications. These snaps are conveniently discoverable and installable from the Snap Store (https://snapcraft.io).

To build Snap-package we need to specify its configuration file — snapcraft.yaml:

name: tlum
summary: (WIP) Open source code cross-platform language learning application
version: 0.0.1+1
description: |
  [DEMO] [Work in Progress: https://github.com/lyskouski/app-language] Open source code cross-platform language 
  learning application leverages the power of natural language processing to pinpoint pronunciation gaps, computer 
  vision to enhance articulation, and machine learning to boost vocabulary proficiency.
base: core22
confinement: strict

parts:
  tlum:
    plugin: python
    source: .
    stage-packages:
      - ffmpeg
      - libportaudio2
      - libpulse0
      - libasound2
    override-build: |
      python3 -m pip install --upgrade pip
      python3 -m pip install --prefix=$SNAPCRAFT_PART_INSTALL -r requirements.txt
      snapcraftctl build
      cp -r src $SNAPCRAFT_PART_INSTALL/
      cp -r assets $SNAPCRAFT_PART_INSTALL/

apps:
  tlum:
    command: bin/python3 $SNAP/src/main.py
    environment:
      KIVY_AUDIO: ffpyplayer
      XDG_RUNTIME_DIR: /tmp
    plugs:
      - home
      - desktop
      - network
      - opengl
      - x11
      - audio-playback
      - pulseaudio
    extensions:
      - gnome
  • **name** → the unique identifier of our Snap (must be globally unique on the Snap Store).
  • **summary** → a one-liner describing our app (shown in the Snap Store).
  • **version* → current version of our app. +1 is a build revision*.
  • **description** → longer text about our project (multiline allowed with |).
  • **base** → runtime base image your Snap depends on. core22 = Ubuntu 22.04 LTS runtime.
  • **confinement: strict** → means the Snap is sandboxed (default). It only gets the permissions explicitly declared in plugs.
  • **parts **specifies required packages/libraries that our application is going to use.
  • **apps** controls the application evaluation.

We can check a package locally before distribution:

# For Linux with snap-support
sudo snap install snapcraft --classic 

# Install Virtual Machine Manager
sudo snap install lxd # required by snapcraft
sudo adduser $USER lxd # grant permissions
newgrp lxd # apply changes
sudo lxd waitready # revise state
sudo lxd init --auto # set up the LXD server

# Build Package
snapcraft pack

# Test generated package
sudo snap install tlum_0.0.1+1.snap --devmode

# Run application
tlum

The next step would be setting up pipelines for Snap package generation and distribution:

- name: Install Snapcraft
  uses: canonical/setup-lxd@v0.1.1

- name: Build Snap
  run: |
      sudo snap install snapcraft --classic
      snapcraft pack --verbose
      cp tlum_0.0.1+1_amd64.snap "$GITHUB_WORKSPACE/tlum_LinuxSnap.snap"

- name: Publish Snap
  env:
    SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_CREDENTIALS }}
  run: |
    sudo --preserve-env=SNAPCRAFT_STORE_CREDENTIALS snapcraft upload tlum_LinuxSnap.snap --release=latest/stable

Where SNAPCRAFT_CREDENTIALS can be taken by the command:

snapcraft login
snapcraft export-login --snaps=tlum --acls package_access,package_push,package_update,package_release credentials.txt

Flatpak package Flatpak (https://flathub.org) is an appealing way to distribute applications because it works across different Linux distributions and handles dependency resolution, ensuring forward compatibility.

We can define a runtime that includes all the common libraries our application requires. The Flatpak is then built on top of this runtime. This approach is powerful because the same runtime can be used consistently across all distributions and shared among multiple applications. Essentially, the runtime provides a stable, reusable foundation for developers and users alike.

The build is controlled by a configuration file — com.tercad.tlum.yml:

app-id: com.tercad.tlum
runtime: org.gnome.Platform
runtime-version: "45"
sdk: org.gnome.Sdk
command: tlum
separate-locales: false
finish-args:
  - --socket=fallback-x11
  - --socket=wayland
  - --socket=pulseaudio
  - --share=network
  - --share=ipc
  - --device=dri
  - --filesystem=xdg-documents/.terCAD:create
modules:
  - name: Tlum
    buildsystem: simple
    only-arches:
      - x86_64
    build-commands:
      - mkdir -p Tlum
      - tar -xf v{VERSION}.tar.gz -C Tlum
      - cp -r Tlum /app/Tlum
      - python -m pip install --upgrade pip
      - python -m pip install -r /app/Tlum/requirements.txt
      - cd /app/Tlum/linux
      - python -m PyInstaller tlum.spec
      - chmod +x /app/Tlum/linux/dist/tlum/tlum
      - mkdir -p /app/bin
      - ln -s /app/Tlum/linux/dist/tlum/tlum /app/bin/tlum
      - mkdir -p /app/bin/assets
      - cp -r /app/Tlum/assets/* /app/bin/assets/
      - export XDG_DATA_HOME="/var/lib/flatpak/exports/share:$XDG_DATA_HOME" 
      - export XDG_DATA_DIRS="/var/lib/flatpak/exports/share:$XDG_DATA_DIRS" 
      - install -Dm644 /app/Tlum/com.tercad.tlum.svg /app/share/icons/hicolor/scalable/apps/com.tercad.tlum.svg
      - install -Dm644 /app/Tlum/com.tercad.tlum.desktop /app/share/applications/com.tercad.tlum.desktop
      - install -Dm644 com.tercad.tlum.metainfo.xml /app/share/appdata/com.tercad.tlum.metainfo.xml
    sources:
      - type: file
        url: https://github.com/lyskouski/app-language/archive/refs/tags/v{VERSION}.tar.gz
        sha256: "{SHA256}"
      - type: file
        path: com.tercad.tlum.metainfo.xml

To build and check the application we might use next sequence of commands:

## Install builder
sudo apt install flatpak-builder

## 'runtime' definition
flatpak search org.freedesktop.Platform
flatpak install flathub org.freedesktop.Platform//23.08 org.freedesktop.Sdk//23.08

## ... or 'gnome', or 'kde'
flatpak search org.gnome.Platform
flatpak install flathub org.gnome.Platform//45 org.gnome.Sdk//45

## Build project from the manifest
flatpak-builder build-dir com.tercad.tlum.yml --force-clean

## Install application (clean if exists)
flatpak-builder --user --install --force-clean build-dir com.tercad.tlum.yml

## Run application
flatpak run com.tercad.tlum

AppImage package AppImage is a cross-platform format for software distribution that requires no installation. Users simply download the .AppImage file, make it executable, and run it.

# appimage-builder recipe see https://appimage-builder.readthedocs.io
version: 1
AppDir:
  path: AppDir
  app_info:
    id: com.tercad.tlum
    name: Tlum
    icon: com.tercad.tlum.svg
    version: 1.0.0
    exec: tlum
    exec_args: $@
  apt:
    arch:
    - amd64
    allow_unauthenticated: true
    sources:
    - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy main restricted
    - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy-updates main restricted
    - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy universe
    - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy-updates universe
    - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy multiverse
    - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy-updates multiverse
    - sourceline: deb http://archive.ubuntu.com/ubuntu/ jammy-backports main restricted
        universe multiverse
    - sourceline: deb http://security.ubuntu.com/ubuntu/ jammy-security main restricted
    - sourceline: deb http://security.ubuntu.com/ubuntu/ jammy-security universe
    - sourceline: deb http://security.ubuntu.com/ubuntu/ jammy-security multiverse
    - sourceline: deb https://ppa.launchpadcontent.net/ondrej/php/ubuntu/ jammy main
    include:
    - libc6:amd64
    - libgtk-3-0
    - ibus-gtk3
    - ffmpeg
    - libpulse0
    - libpulse-mainloop-glib0
    - libasound2
    - libasound2-plugins
    - libportaudio2
    - libsdl2-2.0-0
    - libsdl2-mixer-2.0-0
    - libsndfile1
    - xclip
    - xsel
    exclude:
    - humanity-icon-theme
    - hicolor-icon-theme
    - adwaita-icon-theme
    - ubuntu-mono
  files:
    include: []
    exclude:
    - usr/share/man
    - usr/share/doc/*/README.*
    - usr/share/doc/*/changelog.*
    - usr/share/doc/*/NEWS.*
    - usr/share/doc/*/TODO.*
AppImage:
  arch: x86_64
  update-information: guess

AppImage operates with already compiled solution, so we copy artifacts of Linux build/distribution into the folder AppDir and execute a package creation:

## Install AppImage builder
wget -O appimage-builder-x86_64.AppImage https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage
chmod +x appimage-builder-x86_64.AppImage
## Pack application
appimage-builder-x86_64.AppImage --recipe AppImageBuilder.yml

So, the pipeline steps would look like:

# Linux
      - name: Install system dependencies for Kivy
        if: matrix.target == 'Linux' or matrix.target == 'LinuxAppImage'
        # ... previously shown step

      - name: Build executable
        if: matrix.target == 'Linux' or matrix.target == 'LinuxAppImage'
        # ... previously shown step

# Linux: AppImage
      - name: Patch Manifest
        if: matrix.target == 'LinuxAppImage'
        run: |
          cp -r ./../linux/dist/tlum/* ./AppDir
          tar -xzf fingrom_Linux.tar.gz -C AppDir
          sh patch.sh -v "${{ needs.release.outputs.version }}"
        working-directory: ${{ matrix.build_path }}

      - name: Build Linux AppImage
        if: matrix.target == 'LinuxAppImage'
        run: |
          sudo apt-get install -y libfuse2
          wget -O appimage-builder-x86_64.AppImage https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage
          chmod +x appimage-builder-x86_64.AppImage
          ./appimage-builder-x86_64.AppImage --recipe AppImageBuilder.yml
          cp Tlum_${{ needs.release.outputs.version }}-x86_64.AppImage "$GITHUB_WORKSPACE/tlum_${{ matrix.target }}.AppImage"
        working-directory: ${{ matrix.build_path }}

Considerations When preparing a Kivy application written in Python for Linux distribution, several important factors should be taken into account:

  • Ensure all required Python libraries, assets, and DLLs are included so the app runs without requiring a separate Python installation.
  • Check for potential issues with OpenGL drivers, as Kivy rendering relies on GPU support.
  • If an app requires special capabilities (e.g., camera, microphone, file system access), these must be explicitly declared in the app manifest.
  • Minimize startup time by reducing unnecessary imports or resource-heavy initialization.

Epilogue We’re writing From Zero to Market with Kivy, a practical guide to distributing Kivy apps on Windows, macOS, Linux, iOS, and Android stores. The book will cover packaging, dependencies, platform requirements, performance, UX, best practices, and common pitfalls.

It’s a big project, and we welcome community contributions — whether through sharing experiences, sample projects, testing, or reviewing drafts. Our aim is to create a hands-on, community-driven resource to help developers bring their Kivy apps to a wider audience.

Conclusion From Zero to Market with Kivy is more than just a book — it’s an invitation to the Kivy community to collaborate, learn, and grow together. By sharing knowledge, experiences, and best practices.

This article represents just one chapter in that journey. Your feedback, suggestions, and contributions are highly appreciated, as they help shape a resource that truly reflects the needs and experiences of the community.


메타데이터
post_id
cfd085bad0cb
slug
kivy-python-linux-distribution-linux-snap-flatpak-appimage-cfd085bad0cb
url
https://medium.com/@vlyskouski/kivy-python-linux-distribution-linux-snap-flatpak-appimage-cfd085bad0cb
canonical_url
https://medium.com/@vlyskouski/kivy-python-linux-distribution-linux-snap-flatpak-appimage-cfd085bad0cb
author_url
https://medium.com/@vlyskouski
status
ok
fetched_at
2026-07-17 05:03:42