diff --git a/Gemfile b/Gemfile
index c80ee369..44d9cf77 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,3 +1,14 @@
 source "http://rubygems.org"
 
 gemspec
+
+group :development do
+  gem 'parka'
+  gem 'rake'
+  gem 'ronn'
+  gem 'fakefs', '~> 0.2.1'
+  gem 'rcov',   '~> 0.9.8'
+  gem 'rr',     '~> 1.0.2'
+  gem 'rspec',  '~> 2.6.0'
+  gem "rubyzip"
+end
diff --git a/Gemfile.lock b/Gemfile.lock
index 0ceba4d6..f184e794 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -36,6 +36,7 @@ GEM
     rspec-expectations (2.6.0)
       diff-lcs (~> 1.1.2)
     rspec-mocks (2.6.0)
+    rubyzip (0.9.4)
     term-ansicolor (1.0.6)
     thor (0.14.6)
 
@@ -51,3 +52,4 @@ DEPENDENCIES
   ronn
   rr (~> 1.0.2)
   rspec (~> 2.6.0)
+  rubyzip
diff --git a/Rakefile b/Rakefile
index eeba521a..c1096054 100644
--- a/Rakefile
+++ b/Rakefile
@@ -54,3 +54,115 @@ task :pages => "man:commit" do
     git checkout master
   }
 end
+
+## dist
+
+require "erb"
+require "fileutils"
+require "tmpdir"
+
+def assemble(source, target, perms=0644)
+  FileUtils.mkdir_p(File.dirname(target))
+  File.open(target, "w") do |f|
+    f.puts ERB.new(File.read(source)).result(binding)
+  end
+  File.chmod(perms, target)
+end
+
+def assemble_distribution(target_dir=Dir.pwd)
+  distribution_files.each do |source|
+    target = source.gsub(/^#{project_root}/, target_dir)
+    FileUtils.mkdir_p(File.dirname(target))
+    FileUtils.cp(source, target)
+  end
+end
+
+GEM_BLACKLIST = %w( bundler foreman )
+
+def assemble_gems(target_dir=Dir.pwd)
+  lines = %x{ bundle show }.strip.split("\n")
+  raise "error running bundler" unless $?.success?
+
+  %x{ env BUNDLE_WITHOUT="development:test" bundle show }.split("\n").each do |line|
+    if line =~ /^  \* (.*?) \((.*?)\)/
+      next if GEM_BLACKLIST.include?($1)
+      puts "vendoring: #{$1}-#{$2}"
+      gem_dir = %x{ bundle show #{$1} }.strip
+      FileUtils.mkdir_p "#{target_dir}/vendor/gems"
+      %x{ cp -R "#{gem_dir}" "#{target_dir}/vendor/gems" }
+    end
+  end.compact
+end
+
+def beta?
+  Foreman::VERSION.to_s =~ /pre/
+end
+
+def clean(file)
+  rm file if File.exists?(file)
+end
+
+def distribution_files
+  require "foreman/distribution"
+  Foreman::Distribution.files
+end
+
+def mkchdir(dir)
+  FileUtils.mkdir_p(dir)
+  Dir.chdir(dir) do |dir|
+    yield(File.expand_path(dir))
+  end
+end
+
+def pkg(filename)
+  File.expand_path("../pkg/#{filename}", __FILE__)
+end
+
+def project_root
+  File.dirname(__FILE__)
+end
+
+def resource(name)
+  File.expand_path("../dist/resources/#{name}", __FILE__)
+end
+
+def s3_connect
+  return if @s3_connected
+
+  require "aws/s3"
+
+  unless ENV["HEROKU_RELEASE_ACCESS"] && ENV["HEROKU_RELEASE_SECRET"]
+    puts "please set HEROKU_RELEASE_ACCESS and HEROKU_RELEASE_SECRET in your environment"
+    exit 1
+  end
+
+  AWS::S3::Base.establish_connection!(
+    :access_key_id => ENV["HEROKU_RELEASE_ACCESS"],
+    :secret_access_key => ENV["HEROKU_RELEASE_SECRET"]
+  )
+
+  @s3_connected = true
+end
+
+def store(package_file, filename, bucket="assets.heroku.com")
+  s3_connect
+  puts "storing: #{filename}"
+  AWS::S3::S3Object.store(filename, File.open(package_file), bucket, :access => :public_read)
+end
+
+def tempdir
+  Dir.mktmpdir do |dir|
+    Dir.chdir(dir) do
+      yield(dir)
+    end
+  end
+end
+
+def version
+  require "foreman/version"
+  Foreman::VERSION
+end
+
+Dir[File.expand_path("../dist/**/*.rake", __FILE__)].each do |rake|
+  import rake
+end
diff --git a/dist/gem.rake b/dist/gem.rake
new file mode 100644
index 00000000..1b22ae1d
--- /dev/null
+++ b/dist/gem.rake
@@ -0,0 +1,14 @@
+file pkg("foreman-#{version}.gem") => distribution_files do |t|
+  sh "gem build foreman.gemspec"
+  sh "mv foreman-#{version}.gem #{t.name}"
+end
+
+task "gem:build" => pkg("foreman-#{version}.gem")
+
+task "gem:clean" do
+  clean pkg("foreman-#{version}.gem")
+end
+
+task "gem:release" => "gem:build" do |t|
+  sh "parka push -f #{pkg("foreman-#{version}.gem")}"
+end
diff --git a/dist/pkg.rake b/dist/pkg.rake
new file mode 100644
index 00000000..d06a1347
--- /dev/null
+++ b/dist/pkg.rake
@@ -0,0 +1,52 @@
+require "erb"
+
+file pkg("foreman-#{version}.pkg") => distribution_files do |t|
+  tempdir do |dir|
+    mkchdir("foreman") do
+      assemble_distribution
+      assemble_gems
+      assemble resource("pkg/foreman"), "bin/foreman", 0755
+    end
+
+    kbytes = %x{ du -ks foreman | cut -f 1 }
+    num_files = %x{ find foreman | wc -l }
+
+    mkdir_p "pkg"
+    mkdir_p "pkg/Resources"
+    mkdir_p "pkg/foreman-#{version}.pkg"
+
+    dist = File.read(resource("pkg/Distribution.erb"))
+    dist = ERB.new(dist).result(binding)
+    File.open("pkg/Distribution", "w") { |f| f.puts dist }
+
+    dist = File.read(resource("pkg/PackageInfo.erb"))
+    dist = ERB.new(dist).result(binding)
+    File.open("pkg/foreman-#{version}.pkg/PackageInfo", "w") { |f| f.puts dist }
+
+    mkdir_p "pkg/foreman-#{version}.pkg/Scripts"
+    cp resource("pkg/postinstall"), "pkg/foreman-#{version}.pkg/Scripts/postinstall"
+    chmod 0755, "pkg/foreman-#{version}.pkg/Scripts/postinstall"
+
+    sh %{ mkbom -s foreman pkg/foreman-#{version}.pkg/Bom }
+
+    Dir.chdir("foreman") do
+      sh %{ pax -wz -x cpio . > ../pkg/foreman-#{version}.pkg/Payload }
+    end
+
+    sh %{ pkgutil --flatten pkg foreman-#{version}.pkg }
+
+    cp_r "foreman-#{version}.pkg", t.name
+  end
+end
+
+task "pkg:build" => pkg("foreman-#{version}.pkg")
+
+task "pkg:clean" do
+  clean pkg("foreman-#{version}.pkg")
+end
+
+task "pkg:release" => "pkg:build" do |t|
+  store pkg("foreman-#{version}.pkg"), "foreman/foreman-#{version}.pkg"
+  store pkg("foreman-#{version}.pkg"), "foreman/foreman-beta.pkg" if beta?
+  store pkg("foreman-#{version}.pkg"), "foreman/foreman.pkg" unless beta?
+end
diff --git a/dist/resources/tgz/foreman b/dist/resources/tgz/foreman
new file mode 100644
index 00000000..6475debe
--- /dev/null
+++ b/dist/resources/tgz/foreman
@@ -0,0 +1,15 @@
+#!/usr/bin/env ruby
+
+require "pathname"
+bin_file = Pathname.new(__FILE__).realpath
+
+gem_dir = File.expand_path("../vendor/gems", bin_file)
+  Dir["#{gem_dir}/**/lib"].each do |libdir|
+  $:.unshift libdir
+end
+
+$:.unshift File.expand_path("../lib", bin_file)
+
+require "foreman/cli"
+
+Foreman::CLI.start
diff --git a/dist/tgz.rake b/dist/tgz.rake
new file mode 100644
index 00000000..1f6c8617
--- /dev/null
+++ b/dist/tgz.rake
@@ -0,0 +1,24 @@
+file pkg("foreman-#{version}.tgz") => distribution_files do |t|
+  tempdir do |dir|
+    mkchdir("foreman") do
+      assemble_distribution
+      assemble_gems
+      rm_rf "bin"
+      assemble resource("tgz/foreman"), "foreman", 0755
+    end
+
+    sh "tar czvf #{t.name} foreman"
+  end
+end
+
+task "tgz:build" => pkg("foreman-#{version}.tgz")
+
+task "tgz:clean" do
+  clean pkg("foreman-#{version}.tgz")
+end
+
+task "tgz:release" => "tgz:build" do |t|
+  store pkg("foreman-#{version}.tgz"), "foreman/foreman-#{version}.tgz"
+  store pkg("foreman-#{version}.tgz"), "foreman/foreman-beta.tgz" if beta?
+  store pkg("foreman-#{version}.tgz"), "foreman/foreman.tgz" unless beta?
+end
diff --git a/foreman.gemspec b/foreman.gemspec
index b6222866..aee7e83c 100644
--- a/foreman.gemspec
+++ b/foreman.gemspec
@@ -18,12 +18,4 @@ Gem::Specification.new do |gem|
 
   gem.add_dependency 'term-ansicolor', '~> 1.0.5'
   gem.add_dependency 'thor',           '>= 0.13.6'
-
-  gem.add_development_dependency 'parka'
-  gem.add_development_dependency 'rake'
-  gem.add_development_dependency 'ronn'
-  gem.add_development_dependency 'fakefs', '~> 0.2.1'
-  gem.add_development_dependency 'rcov',   '~> 0.9.8'
-  gem.add_development_dependency 'rr',     '~> 1.0.2'
-  gem.add_development_dependency 'rspec',  '~> 2.6.0'
 end
diff --git a/lib/foreman/distribution.rb b/lib/foreman/distribution.rb
new file mode 100644
index 00000000..2d5650e7
--- /dev/null
+++ b/lib/foreman/distribution.rb
@@ -0,0 +1,9 @@
+module Foreman
+  module Distribution
+    def self.files
+      Dir[File.expand_path("../../../{bin,data,lib}/**/*", __FILE__)].select do |file|
+        File.file?(file)
+      end
+    end
+  end
+end