Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time


Build Status Module Version Hex Docs Total Download License Last Updated

A file attachment/storage library for Elixir.


Add :trunk to your list of dependencies in mix.exs:

def deps do
    {:trunk, "~> 1.0"},

    # If you want to use Amazon S3, then add:
    {:ex_aws_s3, "~> 2.0"},
    {:hackney,   "~> 1.7"},
    {:poison,    "~> 3.1"},
    {:sweet_xml, "~> 0.6"},

Trunk has only one hard dependency on Briefly to handle temporary file creation (and auto-destruction)


Trunk is a behaviour that is implemented in a module. use Trunk creates functions for storing, deleting, and generating urls for your files and their versions. It then implements callbacks in the simplest way possible. You can then override the callbacks you need to as you want to extend the behaviour.

defmodule MyTrunk do
  use Trunk, versions: [:original, :thumb]

  # override callbacks as needed.


Trunk has been designed to be highly configurable. It can be configured in stages with each level merging with the level before it.

See the documentation for all config options.

Global configuration

config :trunk,
  storage: Trunk.Storage.Filesystem,
  storage_opts: [path: "/tmp"]

App specific configuration for umbrella type configs

config :my_app, :trunk,
  storage: Trunk.Storage.S3,
  storage_opts: [bucket: "test-trunk"]

in order for these options to be used, you need to pass the otp_app option when calling use Trunk as follows:

defmodule MyTrunk do
  use Trunk, otp_app: :my_app

Module configuration

defmodule MyTrunk do
  use Trunk, versions: [:original, :trunk],
             storage: Trunk.Storage.Filesystem,
             storage_opts: [path: "/tmp"]

Function options

Caution: If you override options during the storage call, you need to be sure to pass the same options to other calls"/path/to/file.ext", storage: Trunk.Storage.S3, storage_opts: [bucket: "test-trunk"])


Storage is handled by a behaviour.

Two storage handlers are included: Trunk.Storage.Filesystem, and Trunk.Storage.S3 Additional storage systems can be handled by creating a module that implements the Trunk.Storage behaviour.

When storing files, Trunk runs the file through a transformation pipeline allowing you to generate different versions of a file. Each stage in the pipeline is handled via a callback allowing you to configure what transformations take place, where the version is stored and how it is named. Full information can be found in the documentation


You have the option of passing a scope (usually a struct or map) into the transform functions. This scope object will then be available in each callback allowing you to further customise the handling of each version.


defmodule MyTrunk do
  use Trunk, versions: [:thumb]

  def storage_dir(%Trunk.State{scope: %{id: model_id}}, :thumb)
    do: "my_models/#{model_id}"
end"/path/to/file.ext", %{id: 42})
# will save to <storage>/my_models/42/<filename>


State about the file and the version transformations is kept in a Trunk.State struct which is passed to each callback. Each version also keeps track of its own state in a Trunk.VersionState struct which is also available through the Trunk.State.versions map.


One of the key features of Trunk is the ability to take a file and produce transformed versions. Perhaps you want to take an uploaded photo and produce a thumbnail, take a video and extract thumbnails, or take an XLS file and produce a CSV file for easier processing. This is all handled easily with the flexible version and transform system.

See full documentation on Trunk.transform/2


A shout out to stavro who created arc which I used in many of my projects, and which provided much inspiration for what resulted in Trunk.

Photos used in testing

Copyright and License

Copyright (c) 2017 Andrew Timberlake

This work is free. You can redistribute it and/or modify it under the terms of the MIT License. See the file for more details.