diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,9 @@
-## [_Unreleased_](https://github.com/freckle/stackctl/compare/v1.1.4.0...main)
+## [_Unreleased_](https://github.com/freckle/stackctl/compare/v1.2.0.0...main)
+
+## [v1.2.0.0](https://github.com/freckle/stackctl/compare/v1.1.3.1...v1.2.0.0)
+
+- Use more specific types in `Has{Directory,Filter,Color}Option`
+- Add environment variable configuration for `STACKCTL_{DIRECTORY,FILTERS}`
 
 ## [v1.1.4.0](https://github.com/freckle/stackctl/compare/v1.1.3.1...v1.1.4.0)
 
diff --git a/src/Stackctl/CLI.hs b/src/Stackctl/CLI.hs
--- a/src/Stackctl/CLI.hs
+++ b/src/Stackctl/CLI.hs
@@ -87,7 +87,7 @@
     $ defaultLogSettings
 
   logger <- newLogger $ adjustLogSettings
-    (options ^. colorOptionL)
+    (options ^. colorOptionL . to unColorOption)
     (options ^. verboseOptionL)
     envLogSettings
 
diff --git a/src/Stackctl/ColorOption.hs b/src/Stackctl/ColorOption.hs
--- a/src/Stackctl/ColorOption.hs
+++ b/src/Stackctl/ColorOption.hs
@@ -1,5 +1,6 @@
 module Stackctl.ColorOption
-  ( LogColor(..)
+  ( ColorOption(..)
+  , defaultColorOption
   , HasColorOption(..)
   , colorOption
   , colorHandle
@@ -8,29 +9,38 @@
 import Stackctl.Prelude
 
 import Blammo.Logging.LogSettings
+import Data.Semigroup (Last(..))
 import Options.Applicative
 
+newtype ColorOption = ColorOption
+  { unColorOption :: LogColor
+  }
+  deriving Semigroup via Last ColorOption
+
+defaultColorOption :: ColorOption
+defaultColorOption = ColorOption LogColorAuto
+
 class HasColorOption env where
-  colorOptionL :: Lens' env LogColor
+  colorOptionL :: Lens' env ColorOption
 
-instance HasColorOption LogColor where
+instance HasColorOption ColorOption where
   colorOptionL = id
 
-colorOption :: Parser LogColor
-colorOption = option (eitherReader readLogColor) $ mconcat
+colorOption :: Parser ColorOption
+colorOption = option (eitherReader $ fmap ColorOption . readLogColor) $ mconcat
   [ long "color"
   , help "When to colorize output"
   , metavar "auto|always|never"
-  , value LogColorAuto
-  , showDefaultWith showLogColor
+  , value defaultColorOption
+  , showDefaultWith showColorOption
   ]
 
-showLogColor :: LogColor -> String
-showLogColor = \case
+showColorOption :: ColorOption -> String
+showColorOption co = case unColorOption co of
   LogColorAuto -> "auto"
   LogColorAlways -> "always"
   LogColorNever -> "never"
 
-colorHandle :: MonadIO m => Handle -> LogColor -> m Bool
-colorHandle h lc = shouldColorHandle settings h
-  where settings = setLogSettingsColor lc defaultLogSettings
+colorHandle :: MonadIO m => Handle -> ColorOption -> m Bool
+colorHandle h co = shouldColorHandle settings h
+  where settings = setLogSettingsColor (unColorOption co) defaultLogSettings
diff --git a/src/Stackctl/DirectoryOption.hs b/src/Stackctl/DirectoryOption.hs
--- a/src/Stackctl/DirectoryOption.hs
+++ b/src/Stackctl/DirectoryOption.hs
@@ -1,25 +1,41 @@
 module Stackctl.DirectoryOption
-  ( HasDirectoryOption(..)
+  ( DirectoryOption(..)
+  , defaultDirectoryOption
+  , HasDirectoryOption(..)
+  , envDirectoryOption
   , directoryOption
   ) where
 
 import Stackctl.Prelude
 
+import Data.Semigroup (Last(..))
+import qualified Env
 import Options.Applicative
 
+newtype DirectoryOption = DirectoryOption
+  { unDirectoryOption :: FilePath
+  }
+  deriving newtype IsString
+  deriving Semigroup via Last DirectoryOption
+
+defaultDirectoryOption :: DirectoryOption
+defaultDirectoryOption = "."
+
 class HasDirectoryOption env where
-  directoryOptionL :: Lens' env FilePath
+  directoryOptionL :: Lens' env DirectoryOption
 
-instance HasDirectoryOption FilePath where
+instance HasDirectoryOption DirectoryOption where
   directoryOptionL = id
 
-directoryOption :: Parser FilePath
+envDirectoryOption :: Env.Parser Env.Error DirectoryOption
+envDirectoryOption = Env.var (Env.str <=< Env.nonempty) "DIRECTORY"
+  $ Env.help "Operate on specifications in this directory"
+
+directoryOption :: Parser DirectoryOption
 directoryOption = option str $ mconcat
   [ short 'd'
   , long "directory"
   , metavar "PATH"
   , help "Operate on specifications in PATH"
-  , value "."
-  , showDefault
   , action "directory"
   ]
diff --git a/src/Stackctl/FilterOption.hs b/src/Stackctl/FilterOption.hs
--- a/src/Stackctl/FilterOption.hs
+++ b/src/Stackctl/FilterOption.hs
@@ -1,6 +1,8 @@
 module Stackctl.FilterOption
   ( FilterOption
+  , defaultFilterOption
   , HasFilterOption(..)
+  , envFilterOption
   , filterOption
   , filterOptionFromPaths
   , filterOptionFromText
@@ -10,7 +12,9 @@
 import Stackctl.Prelude
 
 import qualified Data.List.NonEmpty as NE
+import Data.Semigroup (Last(..))
 import qualified Data.Text as T
+import qualified Env
 import Options.Applicative
 import Stackctl.AWS.CloudFormation (StackName(..))
 import Stackctl.StackSpec
@@ -20,6 +24,7 @@
 newtype FilterOption = FilterOption
   { unFilterOption :: NonEmpty Pattern
   }
+  deriving Semigroup via Last FilterOption
 
 instance ToJSON FilterOption where
   toJSON = toJSON . showFilterOption
@@ -31,13 +36,19 @@
 instance HasFilterOption FilterOption where
   filterOptionL = id
 
+envFilterOption :: String -> Env.Parser Env.Error FilterOption
+envFilterOption items =
+  Env.var (first Env.UnreadError . readFilterOption) "FILTERS"
+    $ Env.help
+    $ "Filter "
+    <> items
+    <> " by patterns"
+
 filterOption :: String -> Parser FilterOption
 filterOption items = option (eitherReader readFilterOption) $ mconcat
   [ long "filter"
   , metavar "PATTERN[,PATTERN]"
   , help $ "Filter " <> items <> " to match PATTERN(s)"
-  , value defaultFilterOption
-  , showDefaultWith showFilterOption
   ]
 
 filterOptionFromPaths :: NonEmpty FilePath -> FilterOption
diff --git a/src/Stackctl/Options.hs b/src/Stackctl/Options.hs
--- a/src/Stackctl/Options.hs
+++ b/src/Stackctl/Options.hs
@@ -1,10 +1,13 @@
 module Stackctl.Options
-  ( Options(..)
+  ( Options
+  , envParser
   , optionsParser
   ) where
 
 import Stackctl.Prelude
 
+import Data.Semigroup.Generic
+import qualified Env
 import Options.Applicative
 import Stackctl.ColorOption
 import Stackctl.DirectoryOption
@@ -12,29 +15,49 @@
 import Stackctl.VerboseOption
 
 data Options = Options
-  { oDirectory :: FilePath
-  , oFilterOption :: FilterOption
-  , oColor :: LogColor
+  { oDirectory :: Maybe DirectoryOption
+  , oFilter :: Maybe FilterOption
+  , oColor :: Maybe ColorOption
   , oVerbose :: Verbosity
   }
+  deriving stock Generic
+  deriving Semigroup via GenericSemigroupMonoid Options
 
-instance HasDirectoryOption Options where
-  directoryOptionL = lens oDirectory $ \x y -> x { oDirectory = y }
+directoryL :: Lens' Options (Maybe DirectoryOption)
+directoryL = lens oDirectory $ \x y -> x { oDirectory = y }
 
-instance HasColorOption Options where
-  colorOptionL = lens oColor $ \x y -> x { oColor = y }
+filterL :: Lens' Options (Maybe FilterOption)
+filterL = lens oFilter $ \x y -> x { oFilter = y }
 
+colorL :: Lens' Options (Maybe ColorOption)
+colorL = lens oColor $ \x y -> x { oColor = y }
+
+instance HasDirectoryOption Options where
+  directoryOptionL = directoryL . maybeLens defaultDirectoryOption
+
 instance HasFilterOption Options where
-  filterOptionL = lens oFilterOption $ \x y -> x { oFilterOption = y }
+  filterOptionL = filterL . maybeLens defaultFilterOption
 
+instance HasColorOption Options where
+  colorOptionL = colorL . maybeLens defaultColorOption
+
 instance HasVerboseOption Options where
   verboseOptionL = lens oVerbose $ \x y -> x { oVerbose = y }
 
 -- brittany-disable-next-binding
 
+envParser :: Env.Parser Env.Error Options
+envParser = Env.prefixed "STACKCTL_" $ Options
+  <$> optional envDirectoryOption
+  <*> optional (envFilterOption "specifications")
+  <*> pure mempty -- use LOG_COLOR
+  <*> pure mempty -- use LOG_LEVEL
+
+-- brittany-disable-next-binding
+
 optionsParser :: Parser Options
 optionsParser = Options
-  <$> directoryOption
-  <*> filterOption "specifications"
-  <*> colorOption
+  <$> optional directoryOption
+  <*> optional (filterOption "specifications")
+  <*> (Just <$> colorOption)
   <*> verboseOption
diff --git a/src/Stackctl/Prelude.hs b/src/Stackctl/Prelude.hs
--- a/src/Stackctl/Prelude.hs
+++ b/src/Stackctl/Prelude.hs
@@ -1,6 +1,7 @@
 module Stackctl.Prelude
   ( module X
   , decodeUtf8
+  , maybeLens
   ) where
 
 import RIO as X hiding
@@ -30,3 +31,6 @@
 
 decodeUtf8 :: ByteString -> Text
 decodeUtf8 = decodeUtf8With lenientDecode
+
+maybeLens :: a -> Lens' (Maybe a) a
+maybeLens x = lens (fromMaybe x) $ const Just
diff --git a/src/Stackctl/Spec/Capture.hs b/src/Stackctl/Spec/Capture.hs
--- a/src/Stackctl/Spec/Capture.hs
+++ b/src/Stackctl/Spec/Capture.hs
@@ -10,7 +10,7 @@
 import Stackctl.AWS
 import Stackctl.AWS.Scope
 import Stackctl.Config (HasConfig)
-import Stackctl.DirectoryOption (HasDirectoryOption(..))
+import Stackctl.DirectoryOption (HasDirectoryOption(..), unDirectoryOption)
 import Stackctl.Spec.Generate
 import Stackctl.StackSpec
 import System.FilePath.Glob
@@ -74,7 +74,7 @@
   => CaptureOptions
   -> m ()
 runCapture CaptureOptions {..} = do
-  dir <- view directoryOptionL
+  dir <- unDirectoryOption <$> view directoryOptionL
 
   let
     setScopeName scope =
diff --git a/src/Stackctl/Spec/Cat.hs b/src/Stackctl/Spec/Cat.hs
--- a/src/Stackctl/Spec/Cat.hs
+++ b/src/Stackctl/Spec/Cat.hs
@@ -21,7 +21,7 @@
 import Stackctl.AWS.Scope
 import Stackctl.Colors
 import Stackctl.Config (HasConfig)
-import Stackctl.DirectoryOption (HasDirectoryOption(..))
+import Stackctl.DirectoryOption (HasDirectoryOption(..), unDirectoryOption)
 import Stackctl.FilterOption (HasFilterOption)
 import Stackctl.Spec.Discover
 import Stackctl.StackSpec
@@ -67,7 +67,7 @@
   => CatOptions
   -> m ()
 runCat CatOptions {..} = do
-  dir <- view directoryOptionL
+  dir <- unDirectoryOption <$> view directoryOptionL
   colors@Colors {..} <- getColorsStdout
   tree <- specTree <$> discoverSpecs
 
diff --git a/src/Stackctl/Spec/Discover.hs b/src/Stackctl/Spec/Discover.hs
--- a/src/Stackctl/Spec/Discover.hs
+++ b/src/Stackctl/Spec/Discover.hs
@@ -10,7 +10,7 @@
 import Stackctl.AWS
 import Stackctl.AWS.Scope
 import Stackctl.Config (HasConfig)
-import Stackctl.DirectoryOption (HasDirectoryOption(..))
+import Stackctl.DirectoryOption (HasDirectoryOption(..), unDirectoryOption)
 import Stackctl.FilterOption (HasFilterOption(..), filterStackSpecs)
 import Stackctl.StackSpec
 import Stackctl.StackSpecPath
@@ -29,7 +29,7 @@
      )
   => m [StackSpec]
 discoverSpecs = do
-  dir <- view directoryOptionL
+  dir <- unDirectoryOption <$> view directoryOptionL
   scope@AwsScope {..} <- view awsScopeL
   paths <- globRelativeTo
     dir
diff --git a/src/Stackctl/Subcommand.hs b/src/Stackctl/Subcommand.hs
--- a/src/Stackctl/Subcommand.hs
+++ b/src/Stackctl/Subcommand.hs
@@ -7,6 +7,7 @@
 
 import Stackctl.Prelude
 
+import qualified Env
 import Options.Applicative
 import qualified Stackctl.CLI as CLI
 import Stackctl.Colors
@@ -25,17 +26,24 @@
   command (unpack name) (run <$> withInfo description parse)
 
 runSubcommand :: Mod CommandFields (CLI.AppT (CLI.App Options) IO ()) -> IO ()
-runSubcommand = runSubcommand' "Work with Stack specifications" optionsParser
+runSubcommand =
+  runSubcommand' "Work with Stack specifications" envParser optionsParser
 
+-- brittany-disable-next-binding
+
 runSubcommand'
-  :: (HasVerboseOption options, HasColorOption options)
+  :: (Semigroup options, HasVerboseOption options, HasColorOption options)
   => Text
+  -> Env.Parser Env.Error options
   -> Parser options
   -> Mod CommandFields (CLI.AppT (CLI.App options) IO ())
   -> IO ()
-runSubcommand' x op sp = do
-  (options, act) <- execParser $ withInfo x $ (,) <$> op <*> subparser sp
+runSubcommand' title parseEnv parseCLI sp = do
+  (options, act) <- applyEnv
+    <$> Env.parse (Env.header $ unpack title) parseEnv
+    <*> execParser (withInfo title $ (,) <$> parseCLI <*> subparser sp)
   CLI.runAppT options act
+  where applyEnv env = first (env <>)
 
 withInfo :: Text -> Parser a -> ParserInfo a
 withInfo d p = info (p <**> helper) $ progDesc (unpack d) <> fullDesc
diff --git a/src/Stackctl/VerboseOption.hs b/src/Stackctl/VerboseOption.hs
--- a/src/Stackctl/VerboseOption.hs
+++ b/src/Stackctl/VerboseOption.hs
@@ -11,6 +11,7 @@
 import Options.Applicative
 
 newtype Verbosity = Verbosity [()]
+  deriving newtype (Semigroup, Monoid)
 
 verbositySetLogLevels :: Verbosity -> (LogSettings -> LogSettings)
 verbositySetLogLevels (Verbosity bs) = case bs of
diff --git a/stackctl.cabal b/stackctl.cabal
--- a/stackctl.cabal
+++ b/stackctl.cabal
@@ -1,6 +1,6 @@
 cabal-version:   1.18
 name:            stackctl
-version:         1.1.4.0
+version:         1.2.0.0
 license:         MIT
 license-file:    LICENSE
 copyright:       2022 Renaissance Learning Inc
@@ -95,6 +95,7 @@
         cfn-flip >=0.1.0.3,
         conduit,
         containers,
+        envparse,
         errors,
         exceptions,
         extra,
@@ -106,6 +107,7 @@
         optparse-applicative,
         resourcet,
         rio,
+        semigroups,
         text,
         time,
         unliftio,
