How to use jdeb with Maven

April 29, 2026 ยท View on GitHub

Generating a default Debian package with maven is particular easy. Just add the plugin to your POM like this

  <build>
    <plugins>
      <plugin>
        <artifactId>jdeb</artifactId>
        <groupId>org.vafer</groupId>
        <version>1.12</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>jdeb</goal>
            </goals>
            <configuration>
              <dataSet>
                <data>
                  <src>${project.build.directory}/${project.build.finalName}.jar</src>
                  <type>file</type>
                  <mapper>
                    <type>perm</type>
                    <prefix>/usr/share/jdeb/lib</prefix>
                  </mapper>
                </data>
              </dataSet>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

Or if you want to build a custom deb file

  <!-- Indicate it's a deb package which will automatically execute jdeb goal -->
  <packaging>deb</packaging>

  <build>
    <extensions>
      <!-- Add support for the "deb" packaging -->
      <extension>
        <groupId>org.vafer</groupId>
        <artifactId>jdeb</artifactId>
        <version>1.12</version>
      </extension>
    </extensions>
    <pluginManagement>
      <plugins>
        <plugin>
          <artifactId>jdeb</artifactId>
          <groupId>org.vafer</groupId>
          <!-- Customize deb package -->
          <configuration>
            <dataSet>
              <data>
                <src>${project.build.directory}/preparedfiles</src>
                <type>directory</type>
                <mapper>
                  <type>perm</type>
                  <prefix>/usr/lib/xwiki/myproject</prefix>
                </mapper>
              </data>
            </dataSet>
          </configuration>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>

At least the one main control file is required to control the creation of the debian package. This required control file should be found in the control directory (inside the data dir). By default the control file name is also control which gives a path of src/deb/control/control by default. This control file contains the metadata about the Debian package. Usually it will look something along the lines of

Package: [[name]]
Version: [[version]]
Section: misc
Priority: optional
Architecture: all
Depends: jdk (>= 1.5)
Maintainer: Torsten Curdt <torsten@something.com>
Description: jetty java servlet container
Distribution: development
Depends: default-jre | java6-runtime

but check out the example to get a better overview.

If the environment variables DEBEMAIL and DEBFULLNAME are both set this will overrule the Maintainer field set in there. The Installed-Size will also be injected. If a changes file is used, the Distribution usually comes from that file. The default changes file is called CHANGES.txt. See below for the syntax of the content of the changes file.

Property replacement will also occur in the final filename, and any of the standard debian control files: conffiles, preinst, postinst, prerm, postrm. This allows dynamic configuration of the form:

/etc/[[artifactId]]/[[artifactId]].properties
/etc/[[artifactId]]/log4j.xml

Possible substitutions are [[baseDir]], [[buildDir]], [[artifactId]], [[version]], [[extension]] and [[groupId]].

If you now do a mvn clean install, the deb goal will be called and artifacts consisting of the deb and potentially the changes file will automatically be attached to the project.

The jdeb maven plugin also supports a variety of configuration options. These configuration options provide the same features available in the jdeb ant task. To configure the jdeb maven plugin, populate the jdeb configuration section with any of the following options:

ElementDescriptionRequired
debThe debian package to be generatedNo; defaults to [[buildDir]]/[[artifactId]]_[[version]]_all.[[extension]]
typeArtifact typeNo; defaults to deb
classifierArtifact classifierNo; defaults to ''
controlDirThe directory containing the control filesNo; defaults to src/deb/control
installDirThe default directory for the project artifact if no data section is presentNo; defaults to /opt/${artifactId}
dataSetA list of directories, tarballs, or files to include in the deb packageNo; defaults to include your maven artifact
ignoreBrokenLinksIndicates if broken symlinks should be ignored or cause build to failNo; defaults to false
changesInThe changes to addNo
changesOutThe changes file generatedNo
changesSave(NYI) The merged changes fileNo
changesEnabledEnable the creation of the changes fileNo; defaults to true
compression(NYI) Compression method for the data file (gzip, bzip2, xz or none)No; defaults to gzip
digestDigest to use when building the debNo; defaults to SHA256
signPackageIf the debian package should be signedNo
signMethodWhich utility is used for verification (dpkg-sig, debsig-verify)No; defaults to debsig-verify
signRoleDetermines the filename of the signature, debsig only verifies originNo; defaults to origin
signDigestDigest to use for signingNo; defaults to SHA256
signCfgPrefixPrefix for when reading keyring, key and passphrase from settings.xmlNo; defaults to jdeb.
keyringThe file containing the PGP keysNo
keyThe name of the key to be used in the keyringNo
passphraseThe passphrase to use the keyNo
attachAttach artifact to projectNo; defaults to true
snapshotExpandExpand SNAPSHOT into the content of an environment variable or timestamp.No; defaults to false
snapshotEnvName of the environment variable. If it's empty defaults to a timestamp.No; defaults to SNAPSHOT
snapshotTemplateTemplate for replacing the SNAPSHOT value. A timestamp format can be provided in brackets.No;
verboseVerbose loggingNo; defaults to true, will be false in the future
skipIndicates if an execution should be skippedNo; defaults to false
skipSubmodulesSkip goal on all submodulesNo; defaults to false
skipPOMsSkip goal on POM artifactsNo; defaults to true
encodingThe character encoding to use when reading control and changes files.No; defaults to ${project.build.sourceEncoding}

The encoding property does not apply to the "ls" mapper. The mapping files must be encoded as UTF-8.

If you use the dataSet element, you'll need to populate it with a one or more data elements. A data element is used to specify a directory, a tarball archive, or a file. You can add as many data elements to your dataSet as you'd like. The data element has the following options:

ElementDescriptionRequired
srcThe directory, tarball, file to include in the packageYes
dstNew filename at destination (type must be file)No
linkNameThe path of the link (type must be link)Yes for link
linkTargetThe target of the link (type must be link)Yes for link
symlinkIndicate if the link is a symbolic link (type must be link)No; defaults to true
typeType of the data source. (archive, directory, file, files, link or template)No; but will be Yes in the future
missingSrcFail if src file/folder is missing (ignore or fail)No; defaults to fail
includesA comma separated list of files to include from the directory or tarballNo; defaults to all files
excludesA comma separated list of files to exclude from the directory or tarballNo; defaults to no exclusions
conffileA boolean value to define if the files should be included in the conffilesNo; defaults to false
mapperThe files to exclude from the directory or tarballNo
paths/(path..)One or more string literal paths that will created in the packageNo; Yes for type template

There are different kinds of mappers that can be selected via the type argument. The most common one is the 'perm' mapper.

ElementDescriptionRequired
type'perm'Yes
prefixAdd this prefix to the filesNo; defaults to ""
uidNumerical uidNo; defaults to 0
gidNumerical gidNo; defaults to 0
userUser nameNo; defaults to "root"
groupUser groupNo; defaults to "root"
filemodeFile permissions as octetNo; defaults to 644
dirmodeDir permissions as octetNo; defaults to 755
stripStrip n path components from the original fileNo; defaults to 0

Below is an example of how you could configure your jdeb maven plugin to include a directory, a tarball, and a file in your deb package and then sign it with the key 8306FE21 in /home/user/.gnupg/secring.gpg:

  <build>
    <plugins>
      <plugin>
        <artifactId>jdeb</artifactId>
        <groupId>org.vafer</groupId>
        <version>1.12</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>jdeb</goal>
            </goals>
            <configuration>
              <signPackage>true</signPackage>
              <signMethod>dpkg-sig</signMethod>
              <signRole>builder</signRole>
              <keyring>/home/user/.gnupg/secring.gpg</keyring>
              <key>8306FE21</key>
              <passphrase>abcdef</passphrase>

              <dataSet>

                <!-- Tarball example -->
                <data>
                  <src>${project.basedir}/target/my_archive.tar.gz</src>
                  <type>archive</type>
                  <includes>...</includes>
                  <excludes>...</excludes>
                  <mapper>
                    <type>perm</type>
                    <strip>1</strip>
                    <prefix>/somewhere/else</prefix>
                    <user>tcurdt</user>
                    <group>tcurdt</group>
                    <filemode>600</filemode>
                  </mapper>
                </data>

                <!-- Directory example -->
                <data>
                  <src>${project.build.directory}/data</src>
                  <type>directory</type>
                  <includes/>
                  <excludes>**/.svn</excludes>
                  <mapper>
                    <type>ls</type>
                    <src>mapping.txt</src>
                  </mapper>
                </data>

                <!-- File example -->
                <data>
                  <src>${project.basedir}/README.txt</src>
                  <dst>README</dst>
                  <type>file</type>
                  <missingSrc>ignore</missingSrc>
                </data>

                <!-- Multiple files example -->
                <data>
                  <type>files</type>
                  <paths>
                    <path>README.txt</path>
                    <path>CHANGES.txt</path>
                  </paths>
                  <dst>/var/lib/${artifactId}</dst>
                </data>

                <!-- Template example -->
                <data>
                  <type>template</type>
                  <paths>
                    <path>/etc/${artifactId}</path>
                    <path>/var/lib/${artifactId}</path>
                    <path>/var/log/${artifactId}</path>
                    <path>/var/run/${artifactId}</path>
                  </paths>
                </data>

                <!-- Hard link example -->
                <data>
                  <type>link</type>
                  <linkName>/a/path/on/the/target/fs</linkName>
                  <linkTarget>/a/link/to/the/scr/file</linkTarget>
                  <symlink>false</symlink>
                </data>

                <!-- Symbolic link example -->
                <data>
                  <type>link</type>
                  <linkName>/a/path/on/the/target/fs</linkName>
                  <linkTarget>/a/sym/link/to/the/scr/file</linkTarget>
                  <symlink>true</symlink>
                </data>

                <!-- Conffiles example -->
                <data>
                  <src>${project.build.directory}/data</src>
                  <type>directory</type>
                  <includes/>
                  <excludes>**/.svn</excludes>
                  <conffile>true</conffile>
                  <mapper>
                    <type>ls</type>
                    <src>mapping.txt</src>
                  </mapper>
                </data>
              </dataSet>

            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

If you don't want to store your key information in the POM you can store this in your settings.xml, here's an example settings.xml:

  <settings>
    <profiles>
      <profile>
        <id>jdeb-signing</id>
        <properties>
          <jdeb.keyring>/home/user/.gnupg/secring.gpg</jdeb.keyring>
          <jdeb.key>8306FE21</jdeb.key>
          <jdeb.passphrase>abcdef</jdeb.passphrase>
        </properties>
      </profile>
    </profiles>
    <activeProfiles>
      <activeProfile>jdeb-signing</activeProfile>
    </activeProfiles>
  </settings>

keyring, key and passphrase can then be omitted from the POM entirely.

Starting with version 1.10, the jdeb supports reproducible builds. You can add project.build.outputTimestamp to properties in your pom.xml, containing either string formatted as ISO 8601 yyyy-MM-dd'T'HH:mm:ssXXX or as an int representing seconds since the epoch.

  <properties>
    <project.build.outputTimestamp>2021-01-01T12:00:00Z</project.build.outputTimestamp>
  </properties>

You may also use SOURCE_DATE_EPOCH environment variable, containing int representing seconds since the epoch.

Note that if you use both project.build.outputTimestamp in pom.xml and SOURCE_DATE_EPOCH environment variable, the value in pom.xml takes precedence.