diff --git a/Setup.hs b/Setup.hs
--- a/Setup.hs
+++ b/Setup.hs
@@ -1,3 +1,37 @@
 import Distribution.Simple
+import Distribution.Simple.Setup
+import Distribution.Simple.LocalBuildInfo
+import Distribution.PackageDescription
+import Control.Monad (when, forM_)
+import System.Directory
+import System.FilePath
 
-main = defaultMain
+main = defaultMainWithHooks $ simpleUserHooks {
+    postBuild = \args buildflags pkgdesc buildinfo -> do
+       when (buildinfo `has` "portable" || buildinfo `has` "portable-compiler") $ do
+         -- Figure out paths
+         let dirname = "haste-compiler"
+             exes = map fst $ executableConfigs buildinfo
+             builddir = buildDir buildinfo
+             outdir = builddir </> dirname
+             datadir = dataDir $ localPkgDescr buildinfo
+             jsfiles = dataFiles $ localPkgDescr buildinfo
+         
+         dirExists <- doesDirectoryExist outdir
+         when dirExists $ removeDirectoryRecursive outdir
+         createDirectoryIfMissing True (outdir </> "js")
+         
+         -- Copy executables
+         forM_ exes $ \exe -> do
+           copyFile (builddir </> exe </> exe) (outdir </> exe)
+         
+         -- Copy libs
+         forM_ jsfiles $ \js -> do
+           copyFile (datadir </> js) (outdir </> "js" </> js)
+  }
+
+has :: LocalBuildInfo -> String -> Bool
+has lbi fn =
+  case lookup (FlagName fn) (configConfigurationsFlags (configFlags lbi)) of
+    Just f -> f
+    _      -> False
diff --git a/haste-compiler.cabal b/haste-compiler.cabal
--- a/haste-compiler.cabal
+++ b/haste-compiler.cabal
@@ -1,5 +1,5 @@
 Name:           haste-compiler
-Version:        0.2.3
+Version:        0.2.4
 License:        BSD3
 License-File:   LICENSE
 Synopsis:       Haskell To ECMAScript compiler
@@ -10,7 +10,7 @@
                 Bug reports are highly appreciated.
 Category:       Javascript, Compiler, Web
 Cabal-Version:  >= 1.10
-Build-Type:     Simple
+Build-Type:     Custom
 Author:         Anton Ekblad <anton@ekblad.cc>
 Maintainer:     anton@ekblad.cc
 Homepage:       http://github.com/valderman/haste-compiler
@@ -34,6 +34,18 @@
     debug.js
     Canvas.js
 
+Flag portable
+    Description:
+        Install Haste and its package database into a self-contained directory.
+        Primarily useful for installation onto a USB stick.
+    Default: False
+
+Flag portable-compiler
+    Description:
+        Install Haste into a self-contained directory. Package databases are
+        still local to each user. Primarily useful for global installs.
+    Default: False
+
 Executable haste-boot
     Main-Is: haste-boot.hs
     Other-Modules:
@@ -41,7 +53,10 @@
         Haste.Environment
         Control.Shell
     Hs-Source-Dirs: src
-    GHC-Options: -Wall -O2
+    if flag(portable-compiler)
+        CPP-Options: -DPORTABLE_COMPILER
+    if flag(portable)
+        CPP-Options: -DPORTABLE
     Build-Depends:
         ghc,
         base < 5,
@@ -56,12 +71,17 @@
         time,
         transformers,
         network,
-        HTTP
+        HTTP,
+        executable-path
     Default-Language: Haskell98
 
 Executable hastec
     Hs-Source-Dirs: src
     GHC-Options: -Wall -O2
+    if flag(portable-compiler)
+        CPP-Options: -DPORTABLE_COMPILER
+    if flag(portable)
+        CPP-Options: -DPORTABLE
     Build-Depends:
         base < 5,
         ghc-prim,
@@ -77,7 +97,8 @@
         ghc-paths,
         process,
         random,
-        system-fileio
+        system-fileio,
+        executable-path
     Main-Is:
         Main.hs
     Other-Modules:
@@ -109,31 +130,46 @@
 Executable haste-inst
     Main-Is: haste-inst.hs
     Hs-Source-Dirs: src
+    if flag(portable-compiler)
+        CPP-Options: -DPORTABLE_COMPILER
+    if flag(portable)
+        CPP-Options: -DPORTABLE
     Build-Depends:
         base < 5,
         filepath,
         process,
-        directory
+        directory,
+        executable-path
     default-language: Haskell98
 
 Executable haste-pkg
     Main-Is: haste-pkg.hs
     Hs-Source-Dirs: src
+    if flag(portable-compiler)
+        CPP-Options: -DPORTABLE_COMPILER
+    if flag(portable)
+        CPP-Options: -DPORTABLE
     Build-Depends:
         base < 5,
         process,
         filepath,
-        directory
+        directory,
+        executable-path
     default-language: Haskell98
 
 Executable haste-install-his
     Main-Is: haste-install-his.hs
     Hs-Source-Dirs: src
+    if flag(portable-compiler)
+        CPP-Options: -DPORTABLE_COMPILER
+    if flag(portable)
+        CPP-Options: -DPORTABLE
     Build-Depends:
         base < 5,
         filepath,
         directory,
-        process
+        process,
+        executable-path
     default-language: Haskell98
 
 Executable haste-copy-pkg
@@ -142,6 +178,10 @@
         Haste.Environment
         Control.Shell
     Hs-Source-Dirs: src
+    if flag(portable-compiler)
+        CPP-Options: -DPORTABLE_COMPILER
+    if flag(portable)
+        CPP-Options: -DPORTABLE
     Build-Depends:
         base < 5,
         filepath,
@@ -149,5 +189,6 @@
         process,
         temporary,
         time,
-        transformers
+        transformers,
+        executable-path
     default-language: Haskell98
diff --git a/src/ArgSpecs.hs b/src/ArgSpecs.hs
--- a/src/ArgSpecs.hs
+++ b/src/ArgSpecs.hs
@@ -5,8 +5,7 @@
 import Haste.Environment
 import Data.JSTarget.PP (debugPPOpts)
 import Data.List (isSuffixOf)
-import System.IO.Unsafe (unsafePerformIO)
-import Paths_haste_compiler (getDataFileName)
+import System.FilePath ((</>))
 
 argSpecs :: [ArgSpec Config]
 argSpecs = [
@@ -145,5 +144,5 @@
     cfg {rtsLibs = unicode : filter (not . (cheap `isSuffixOf`)) libs}
   where
     libs = rtsLibs cfg
-    unicode = unsafePerformIO $ getDataFileName "unicode.js"
-    cheap = unsafePerformIO $ getDataFileName "cheap-unicode.js"
+    unicode = jsDir </> "unicode.js"
+    cheap = jsDir </> "cheap-unicode.js"
diff --git a/src/Haste/Config.hs b/src/Haste/Config.hs
--- a/src/Haste/Config.hs
+++ b/src/Haste/Config.hs
@@ -3,9 +3,7 @@
   Config (..), AppStart, defConfig, stdJSLibs, startCustom, fastMultiply,
   safeMultiply, debugLib) where
 import Data.JSTarget
-import System.IO.Unsafe (unsafePerformIO)
-import System.FilePath (replaceExtension)
-import Paths_haste_compiler (getDataFileName)
+import System.FilePath (replaceExtension, (</>))
 import DynFlags
 import Data.ByteString.Lazy.Builder
 import Data.Monoid
@@ -14,13 +12,13 @@
 type AppStart = Builder -> Builder
 
 stdJSLibs :: [FilePath]
-stdJSLibs = unsafePerformIO $ mapM getDataFileName [
+stdJSLibs = map (jsDir </>)  [
     "rts.js", "stdlib.js", "MVar.js", "StableName.js", "Integer.js", "md5.js",
     "array.js", "pointers.js", "cheap-unicode.js", "Canvas.js"
   ]
 
 debugLib :: FilePath
-debugLib = unsafePerformIO $ getDataFileName "debug.js"
+debugLib = jsDir </> "debug.js"
 
 -- | Execute the program as soon as it's loaded into memory.
 --   Evaluate the result of applying main, as we might get a thunk back if
diff --git a/src/Haste/Environment.hs b/src/Haste/Environment.hs
--- a/src/Haste/Environment.hs
+++ b/src/Haste/Environment.hs
@@ -1,43 +1,63 @@
+{-# LANGUAGE CPP #-}
 -- | Paths, host bitness and other environmental information about Haste.
-module Haste.Environment where
+module Haste.Environment (hasteDir, jsmodDir, hasteInstDir, pkgDir, pkgLibDir,
+                          jsDir, hostWordSize, runAndWait, hasteBinary,
+                          hastePkgBinary, hasteInstHisBinary, hasteInstBinary,
+                          hasteCopyPkgBinary, closureCompiler) where
 import System.Process
 import System.IO.Unsafe
-import System.Directory
 import System.FilePath
 import Data.Bits (bitSize)
 import Foreign.C.Types (CIntPtr)
+import System.Environment.Executable
+import System.Directory
+import Paths_haste_compiler
 
--- | Host word size in bits.
-hostWordSize :: Int
-hostWordSize = bitSize (undefined :: CIntPtr)
+-- | The directory where the currently residing binary lives.
+currentBinDir :: FilePath
+currentBinDir = dropFileName . unsafePerformIO $ getExecutablePath
 
--- | Directory where cabal resides. Bundled JS files end up here.
-cabalDir :: FilePath
-cabalDir = unsafePerformIO $ getAppUserDataDirectory "cabal"
+#if defined(PORTABLE) || defined(PORTABLE_COMPILER)
+hasteBinDir :: FilePath
+hasteBinDir = currentBinDir
 
--- | Cabal dir for binaries.
-cabalBinDir :: FilePath
-cabalBinDir = cabalDir </> "bin"
+jsDir :: FilePath
+jsDir = hasteBinDir </> "js"
+#else
+hasteBinDir :: FilePath
+hasteBinDir = unsafePerformIO $ getBinDir
 
--- | Directory housing hi files, jsmods and other stuff Haste spits out.
+jsDir :: FilePath
+jsDir = unsafePerformIO $ getDataDir
+#endif
+
+#if defined(PORTABLE)
 hasteDir :: FilePath
+hasteDir = hasteBinDir
+#else
+hasteDir :: FilePath
 hasteDir = unsafePerformIO $ getAppUserDataDirectory "haste"
+#endif
 
 jsmodDir :: FilePath
-jsmodDir = hasteDir </> "lib"
-
--- | Directory containing library information. 
-pkgLibDir :: FilePath
-pkgLibDir = hasteInstDir </> "lib"
+jsmodDir = hasteDir </> "jsmods"
 
 -- | Base directory for haste-inst.
 hasteInstDir :: FilePath
-hasteInstDir = hasteDir </> "haste-install"
+hasteInstDir = hasteDir </> "libraries"
 
 -- | Directory housing package information.
 pkgDir :: FilePath
-pkgDir = hasteDir </> "haste-pkg"
+pkgDir = hasteDir </> "packages"
 
+-- | Host word size in bits.
+hostWordSize :: Int
+hostWordSize = bitSize (undefined :: CIntPtr)
+
+-- | Directory containing library information. 
+pkgLibDir :: FilePath
+pkgLibDir = hasteInstDir </> "lib"
+
 -- | Run a process and wait for its completion.
 runAndWait :: FilePath -> [String] -> Maybe FilePath -> IO ()
 runAndWait file args workDir = do
@@ -45,6 +65,7 @@
   _ <- waitForProcess h
   return ()
 
+{-
 -- | Find an executable.
 locateBinary :: String -> [FilePath] -> IO (Either String FilePath)
 locateBinary progname (c:cs) = do
@@ -55,21 +76,34 @@
 locateBinary progname _ = do
   return $ Left $ "No " ++ progname ++ " executable found; aborting!"
 
--- | Find a binary that's probably in Cabal's bin directory.
+-- | Find a binary.
 binaryPath :: FilePath -> FilePath
 binaryPath exe = unsafePerformIO $ do
-  b <- locateBinary exe [exe, cabalBinDir </> exe]
+  b <- locateBinary exe [exe, currentBinDir </> exe, cabalBinDir </> exe]
   case b of
     Left err   -> error err
     Right path -> return path
+-}
 
 -- | The main Haste compiler binary.
 hasteBinary :: FilePath
-hasteBinary = binaryPath "hastec"
+hasteBinary = hasteBinDir </> "hastec"
 
 -- | Binary for haste-pkg.
 hastePkgBinary :: FilePath
-hastePkgBinary = binaryPath "haste-pkg"
+hastePkgBinary = hasteBinDir </> "haste-pkg"
+
+-- | Binary for haste-copy-pkg.
+hasteCopyPkgBinary :: FilePath
+hasteCopyPkgBinary = hasteBinDir </> "haste-copy-pkg"
+
+-- | Binary for haste-pkg.
+hasteInstBinary :: FilePath
+hasteInstBinary = hasteBinDir </> "haste-inst"
+
+-- | Binary for haste-install-his.
+hasteInstHisBinary :: FilePath
+hasteInstHisBinary = hasteBinDir </> "haste-install-his"
 
 -- | JAR for Closure compiler.
 closureCompiler :: FilePath
diff --git a/src/Haste/Version.hs b/src/Haste/Version.hs
--- a/src/Haste/Version.hs
+++ b/src/Haste/Version.hs
@@ -10,7 +10,7 @@
 import Haste.Environment (hasteDir)
 
 hasteVersion :: Version
-hasteVersion = Version [0, 2, 3] []
+hasteVersion = Version [0, 2, 4] []
 
 ghcVersion :: String
 ghcVersion = cProjectVersion
diff --git a/src/haste-boot.hs b/src/haste-boot.hs
--- a/src/haste-boot.hs
+++ b/src/haste-boot.hs
@@ -68,9 +68,12 @@
   when (getLibs cfg) $ do
     when (not $ useLocalLibs cfg) $ do
       fetchLibs tmpdir
-    exists <- doesDirectoryExist hasteDir
-    when exists $ do
-      removeDirectoryRecursive hasteDir
+    exists <- doesDirectoryExist hasteInstDir
+    when exists $ removeDirectoryRecursive hasteInstDir
+    exists <- doesDirectoryExist jsmodDir
+    when exists $ removeDirectoryRecursive jsmodDir
+    exists <- doesDirectoryExist pkgDir
+    when exists $ removeDirectoryRecursive pkgDir
     buildLibs
   when (getClosure cfg) $ do
     installClosure
@@ -117,15 +120,15 @@
       -- Set up dirs and copy includes
       mkdir True $ pkgLibDir
       cpDir "include" hasteDir
-      run_ "haste-pkg" ["update", "libraries" </> "rts.pkg"] ""
+      run_ hastePkgBinary ["update", "libraries" </> "rts.pkg"] ""
       
       inDirectory "libraries" $ do
         -- Install ghc-prim
         inDirectory "ghc-prim" $ do
           hasteInst ["configure"]
           hasteInst ["build", "--install-jsmods", ghcOpts]
-          run_ "haste-install-his" ["ghc-prim-0.3.0.0", "dist" </> "build"] ""
-          run_ "haste-pkg" ["update", "packageconfig"] ""
+          run_ hasteInstHisBinary ["ghc-prim-0.3.0.0", "dist" </> "build"] ""
+          run_ hastePkgBinary ["update", "packageconfig"] ""
         
         -- Install integer-gmp; double install shouldn't be needed anymore.
         inDirectory "integer-gmp" $ do
@@ -143,8 +146,8 @@
           hasteInst ["build", "--install-jsmods", ghcOpts]
           let base = "base-" ++ basever
               pkgdb = "--package-db=dist" </> "package.conf.inplace"
-          run_ "haste-install-his" [base, "dist" </> "build"] ""
-          run_ "haste-copy-pkg" [base, pkgdb] ""
+          run_ hasteInstHisBinary [base, "dist" </> "build"] ""
+          run_ hasteCopyPkgBinary [base, pkgdb] ""
         
         -- Install array, fursuit and haste-lib
         forM_ ["array", "fursuit", "haste-lib"] $ \pkg -> do
@@ -156,4 +159,4 @@
     ghcOpts =
       "--ghc-options=-DHASTE_HOST_WORD_SIZE_IN_BITS=" ++ show hostWordSize
     hasteInst args =
-      run_ "haste-inst" ("--unbooted" : args) ""
+      run_ hasteInstBinary ("--unbooted" : args) ""
diff --git a/src/haste-copy-pkg.hs b/src/haste-copy-pkg.hs
--- a/src/haste-copy-pkg.hs
+++ b/src/haste-copy-pkg.hs
@@ -22,7 +22,7 @@
 copyFromDB :: [String] -> String -> Shell ()
 copyFromDB pkgdbs package = do
   pkgdesc <- run "ghc-pkg" (["describe", package] ++ pkgdbs) ""
-  run_ "haste-pkg" ["update", "-", "--force"] (fixPaths package pkgdesc)
+  run_ hastePkgBinary ["update", "-", "--force"] (fixPaths package pkgdesc)
 
 -- | Hack a config to work with Haste.
 fixPaths :: String -> String -> String
