Go parser for Systemd unit files.
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
onlyati 9e8a2a35e8
All checks were successful
ci/woodpecker/push/change_log Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/tag/release Pipeline was successful
ci/woodpecker/cron/vulnerability Pipeline was successful
ci/woodpecker/cron/auto_merge Pipeline was successful
ci/woodpecker/cron/renovate Pipeline was successful
ci/woodpecker/cron/release Pipeline was successful
fix: Blank line after a backslash continuation swallows the next key (#21)
A cont_sign ('\\') is meant to fold only the single line right after it, but the lexer inferred "still folding" from whether the last emitted token was a cont_sign. Blank lines emit no token, so that check stayed true across any number of blank lines and even leaked into the next, unrelated keyword's parse (readKeyValuePair is re-entered once per keyword), silently merging it into the previous value.

Track fold state explicitly on the Lexer instead of inferring it from the token stream, so a continuation ends after exactly one folded line.

Reviewed-on: #21
2026-09-14 21:11:10 +00:00
dist fix: Accept Systemd time span syntax when decoding time.Duration fields besides Go native format (#18) 2026-09-14 19:35:26 +00:00
encoding/systemd fix: Blank line after a backslash continuation swallows the next key (#21) 2026-09-14 21:11:10 +00:00
testutils feat: Basic implementation 2026-08-29 18:11:56 +02:00
.gitignore feat: Basic implementation 2026-08-29 18:11:56 +02:00
.markdownlint.json feat: Basic implementation 2026-08-29 18:11:56 +02:00
.prettierrc feat: Basic implementation 2026-08-29 18:11:56 +02:00
go.mod maint: Update module github.com/go-openapi/testify/v2 to v2.8.0 #12 2026-09-07 21:34:10 +00:00
go.sum maint: Update module github.com/go-openapi/testify/v2 to v2.8.0 #12 2026-09-07 21:34:10 +00:00
LICENSE feat: Basic implementation 2026-08-29 18:11:56 +02:00
README.md fix: Marshal the map besides the struct (#5) 2026-08-29 17:17:44 +00:00
renovate.json ci: Add renovate.json (#7) 2026-08-29 17:36:00 +00:00

Systemd unit file parser

This is a parser library to parse Systemd unit files, which can be any unit file or Podman Quadlet or any configuration file that wants to follow this pattern.

The interface is very similar than encoding/json in the standard library.

Install

Run the following command:

go get -u code.thinkaboutit.tech/pandora/systemd-file.gopack

Examples to use

Parse existing file with specific struct

If we had the following unit file:

[Unit]
SourcePath=/etc/fstab
Documentation=man:fstab(5) man:systemd-fstab-generator(8)

[Automount]
Where=/var/mnt/data

[X-Other]
Property=Value is here
Property=Another value is here

We can parse it from Go:

type AutoMountFile struct {
    Unit      AutoMountUnit
    Automount AutoMount
    XOther    XOther `systemd:"X-Other"`
}

type AutoMountUnit struct {
    SourcePath    string
    Documentation string
}

type AutoMount struct {
    Where string
}

type XOther struct {
    Property []string
}

func main() {
    input, err := os.ReadFile("path/to/test.automount")
    if err != nil {
        fmt.Println(err.Error())
        os.Exit(1)
    }

    var file AutoMountFile
    err = systemd.Unmarshal([]byte(input), &file)
    if err != nil {
        fmt.Println(err.Error())
        os.Exit(1)
    }
    fmt.Printf("%+v\n", file)
}

Read the whole file

input = `
[Setup]
TargetRegistry=code.thinkaboutit.tech
ProdOwner=pandora
StagingOwner=staging

[WolifBase]
Source=cgr.dev/chainguard/wolfi-base:latest@sha256:195c8853f52e0463d217aa8bdf14f6730ae97305de399559d86b76403f9528af

[DebianBase]
Source=dhi.io/debian-base:trixie@sha256:5a539a75a33da9019e8f667c3e7e3e1eb00b52a69cace95b0225f8134aee17b3
TargetName=debian-base

[DebianDev]
Source=dhi.io/debian-base:trixie-dev@sha256:57c88d9180b30314a04650426af8d4301c5c9738e4c5672a1db99a03f6a54721
TargetName=debian-dev
`

var result map[string]map[string]any
err = systemd.Unmarshal([]byte(input), &result)
if err != nil {
    fmt.Println(err.Error())
    os.Exit(1)
}

for sName, sValue := range result {
    for k, v := range sValue {
        fmt.Printf("%s.%s ==> %v\n", sName, k, v)
    }
}

Convert map to systemd file

The library can parse map or struct to string in Systemd file format.

data := map[string]any{
    "Service": map[string]any{
        "Restart":    "always",
        "TimeoutSec": 30,
        "Environment": []string{
            "ENV=prod",
            "DEBUG=false",
        },
    },
}

marshaled, err := systemd.Marshal(data)
require.NoError(t, err)

expected := `[Service]
Environment=ENV=prod
Environment=DEBUG=false
Restart=always
TimeoutSec=30
`
assert.Equal(t, expected, string(marshaled))