diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,22 @@
 
 ## Unreleased
 
+- [#23](https://github.com/parsonsmatt/hotel-california/pull/23)
+    - Tracing is now only initialized when an exporter is configured;
+      otherwise tracing is bypassed entirely. Honeycomb is no longer consulted
+      to decide whether tracing is on. Following the [OpenTelemetry
+      environment variable specification](https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/):
+        - `OTEL_SDK_DISABLED=true` (case-insensitive) disables tracing.
+        - `OTEL_TRACES_EXPORTER=none` disables tracing; any other non-empty
+          value enables it.
+        - Otherwise, tracing is enabled iff any `OTEL_EXPORTER_*` environment
+          variable is set with a non-empty value.
+        - Environment variables set to the empty string are treated as unset.
+    - Breaking change: the callback to `withGlobalTracing` now receives a
+      `TracingStatus` record (instead of `Maybe HoneycombTarget`), which
+      carries a `tracingEnabled` field. Downstream consumers that want
+      Honeycomb trace links can set those up themselves.
+
 ## 0.0.6.2 - 2026-06-25
 
 - [#25](https://github.com/parsonsmatt/hotel-california/pull/25)
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -39,6 +39,11 @@
   [`hs-opentelemetry-sdk`](https://hackage.haskell.org/package/hs-opentelemetry-sdk)
   for more information
 
+Tracing is opt-in: it is enabled when `OTEL_TRACES_EXPORTER` is set (to
+anything other than `none`), or when any `OTEL_EXPORTER_*` environment
+variable is set with a non-empty value. Setting `OTEL_SDK_DISABLED=true`
+disables tracing regardless of any other configuration.
+
 # Background/FAQ
 
 ## Lol what's up with the name
diff --git a/Setup.hs b/Setup.hs
--- a/Setup.hs
+++ b/Setup.hs
@@ -1,2 +1,3 @@
 import Distribution.Simple
+
 main = defaultMain
diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -1,11 +1,11 @@
 module Main where
 
 import Data.Version (showVersion)
-import Paths_hotel_california (version)
 import HotelCalifornia.Exec
-import HotelCalifornia.Tracing (withGlobalTracing)
+import HotelCalifornia.Tracing (TracingStatus (..), withGlobalTracing)
 import Options.Applicative
 import Options.Applicative.Help.Pretty (Doc, vsep)
+import Paths_hotel_california (version)
 
 data Command = Command
     { commandGlobalOptions :: GlobalOptions
@@ -18,49 +18,58 @@
     = Exec ExecArgs
 
 programDescription :: Doc
-programDescription = vsep
-  [ "`hotel-california` is a tool for OTel tracing of shell scripts, inspired by `otel-cli`."
-  , "For help with a command, say `hotel COMMAND --help`. Currently, the only supported command is `exec`."
-  , ""
-  , "Check out the repository any time you like at https://github.com/parsonsmatt/hotel-california."
-  ]
+programDescription =
+    vsep
+        [ "`hotel-california` is a tool for OTel tracing of shell scripts, inspired by `otel-cli`."
+        , "For help with a command, say `hotel COMMAND --help`. Currently, the only supported command is `exec`."
+        , ""
+        , "Check out the repository any time you like at https://github.com/parsonsmatt/hotel-california."
+        ]
 
 optionsParser :: ParserInfo Command
 optionsParser =
     info' parser' programDescription
   where
-  -- thanks danidiaz for the blog post
+    -- thanks danidiaz for the blog post
     info' :: Parser a -> Doc -> ParserInfo a
-    info' p descDoc = info
-        (helper <*> p)
-        (fullDesc <> progDescDoc (Just descDoc) <> noIntersperse)
+    info' p descDoc =
+        info
+            (helper <*> p)
+            (fullDesc <> progDescDoc (Just descDoc) <> noIntersperse)
 
     parser' :: Parser Command
     parser' =
         Command
             <$> generalOptionsParser
             <*> subCommandParser
-            <**> simpleVersioner (showVersion version)
+                <**> simpleVersioner (showVersion version)
 
     generalOptionsParser =
         pure GlobalOptions
 
     subCommandParser :: Parser SubCommand
     subCommandParser =
-        subparser $ foldMap command'
-                    [ ("exec", "Execute the given command with tracing enabled", Exec <$> parseExecArgs)
-                    ]
+        subparser $
+            foldMap
+                command'
+                [
+                    ( "exec"
+                    , "Execute the given command with tracing enabled"
+                    , Exec <$> parseExecArgs
+                    )
+                ]
 
-    command' (cmdName,desc,parser) =
+    command' (cmdName, desc, parser) =
         command cmdName (info' parser desc)
 
 main :: IO ()
 main = do
-    withGlobalTracing $ \mTarget -> do
-        let parserPrefs = defaultPrefs{ prefMultiSuffix = "..." }
-        Command {..} <- customExecParser parserPrefs optionsParser
+    withGlobalTracing $ \tracingStatus -> do
+        let
+            parserPrefs = defaultPrefs{prefMultiSuffix = "..."}
+        Command{..} <- customExecParser parserPrefs optionsParser
         case commandSubCommand of
             Exec execArgs ->
-                case mTarget of
-                    Just _target -> runExecArgs execArgs
-                    Nothing -> runNoTracing $ execArgsSubprocess execArgs
+                if tracingStatus.tracingEnabled
+                    then runExecArgs execArgs
+                    else runNoTracing $ execArgsSubprocess execArgs
diff --git a/hotel-california.cabal b/hotel-california.cabal
--- a/hotel-california.cabal
+++ b/hotel-california.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           hotel-california
-version:        0.0.6.2
+version:        0.1.0.0
 description:    Please see the README on GitHub at <https://github.com/parsonsmatt/hotel-california#readme>
 homepage:       https://github.com/parsonsmatt/hotel-california#readme
 bug-reports:    https://github.com/parsonsmatt/hotel-california/issues
@@ -77,12 +77,11 @@
     , bytestring
     , directory
     , filepath
-    , hs-opentelemetry-api >=0.1.0.0
+    , hs-opentelemetry-api >=0.3.0.0
     , hs-opentelemetry-exporter-otlp
     , hs-opentelemetry-propagator-w3c
-    , hs-opentelemetry-sdk >=0.0.3.6
+    , hs-opentelemetry-sdk >=0.1.0.1
     , hs-opentelemetry-utils-exceptions
-    , hs-opentelemetry-vendor-honeycomb
     , http-types
     , optparse-applicative
     , posix-escape
@@ -143,12 +142,11 @@
     , directory
     , filepath
     , hotel-california
-    , hs-opentelemetry-api >=0.1.0.0
+    , hs-opentelemetry-api >=0.3.0.0
     , hs-opentelemetry-exporter-otlp
     , hs-opentelemetry-propagator-w3c
-    , hs-opentelemetry-sdk >=0.0.3.6
+    , hs-opentelemetry-sdk >=0.1.0.1
     , hs-opentelemetry-utils-exceptions
-    , hs-opentelemetry-vendor-honeycomb
     , http-types
     , optparse-applicative
     , posix-escape
@@ -210,12 +208,11 @@
     , directory
     , filepath
     , hotel-california
-    , hs-opentelemetry-api >=0.1.0.0
+    , hs-opentelemetry-api >=0.3.0.0
     , hs-opentelemetry-exporter-otlp
     , hs-opentelemetry-propagator-w3c
-    , hs-opentelemetry-sdk >=0.0.3.6
+    , hs-opentelemetry-sdk >=0.1.0.1
     , hs-opentelemetry-utils-exceptions
-    , hs-opentelemetry-vendor-honeycomb
     , http-types
     , optparse-applicative
     , posix-escape
diff --git a/src/HotelCalifornia/Tracing.hs b/src/HotelCalifornia/Tracing.hs
--- a/src/HotelCalifornia/Tracing.hs
+++ b/src/HotelCalifornia/Tracing.hs
@@ -6,9 +6,10 @@
     ) where
 
 import Control.Monad
-import Data.ByteString.Char8 qualified as BS8
+import Data.Char (toLower)
+import Data.List (isPrefixOf)
+import Data.Maybe (isJust)
 import Data.Text (Text)
-import Data.Time
 import HotelCalifornia.Tracing.TraceParent
 import OpenTelemetry.Context as Context hiding (lookup)
 import OpenTelemetry.Context.ThreadLocal (attachContext)
@@ -23,7 +24,7 @@
     , inSpan''
     )
 import OpenTelemetry.Trace qualified as Trace
-import OpenTelemetry.Vendor.Honeycomb qualified as Honeycomb
+import System.Environment (getEnvironment)
 import UnliftIO
 
 -- | Initialize the global tracing provider for the application and run an action
@@ -31,29 +32,61 @@
 --   up the provider afterwards.
 --
 --   This also sets up an empty context (creating a new trace ID).
-withGlobalTracing
-    :: (MonadUnliftIO m) => (Maybe Honeycomb.HoneycombTarget -> m a) -> m a
+--
+--   The callback receives a 'TracingStatus' describing whether tracing was
+--   actually initialized; see 'tracingEnabled'.
+withGlobalTracing :: (MonadUnliftIO m) => (TracingStatus -> m a) -> m a
 withGlobalTracing act = do
     void $ attachContext Context.empty
     liftIO setParentSpanFromEnvironment
-    withTracer $ \_ -> do
-        -- note: this is not in a span since we don't have a root span yet so it
-        -- would not wind up in the trace in a helpful way anyway
-        mTarget <-
-            Honeycomb.getOrInitializeHoneycombTargetInContext initializationTimeout
-                `catch` \(e :: SomeException) -> do
-                    -- we are too early in initialization to be able to use a normal logger,
-                    -- but this needs to get out somehow.
-                    --
-                    -- honeycomb links are not load-bearing, so we let them just not come
-                    -- up if the API fails.
-                    liftIO . BS8.hPutStrLn stderr $
-                        "error setting up Honeycomb trace links: " <> (BS8.pack $ displayException e)
-                    pure Nothing
+    enabled <- liftIO otelTracingEnabled
+    if enabled
+        then withTracer $ \_ -> act TracingStatus{tracingEnabled = True}
+        else act TracingStatus{tracingEnabled = False}
 
-        act mTarget
+-- | The result of setting up tracing, passed to the callback of
+--   'withGlobalTracing'.
+data TracingStatus = TracingStatus
+    { tracingEnabled :: Bool
+    -- ^ 'True' when an exporter is configured (see 'otelTracingEnabled') and
+    --   tracing has been initialized, and 'False' otherwise -- in which case
+    --   the caller should bypass tracing.
+    }
+
+-- | Decide whether tracing should be initialized, following the OpenTelemetry
+--   [environment variable specification](https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/)
+--   where practical:
+--
+--   * @OTEL_SDK_DISABLED=true@ (case-insensitive) disables tracing.
+--   * @OTEL_TRACES_EXPORTER=none@ disables tracing; any other non-empty value
+--     enables it.
+--   * Otherwise, tracing is enabled iff any @OTEL_EXPORTER_*@ environment
+--     variable is set with a non-empty value.
+--
+--   The spec would have tracing enabled unconditionally, with
+--   @OTEL_TRACES_EXPORTER@ defaulting to an OTLP exporter aimed at
+--   @localhost@; since running with no collector at all is the common case
+--   for a CLI tool, we deviate and treat exporter configuration as opt-in.
+--
+--   Per the spec, an environment variable set to the empty string is treated
+--   the same as unset.
+otelTracingEnabled :: IO Bool
+otelTracingEnabled = do
+    env <- getEnvironment
+    let
+        getVar key = do
+            value <- lookup key env
+            guard $ not $ null value
+            pure $ map toLower value
+        sdkDisabled = getVar "OTEL_SDK_DISABLED" == Just "true"
+        tracesExporter = getVar "OTEL_TRACES_EXPORTER"
+        hasOtelExporterVar = any isOtelExporterVar env
+    pure $
+        not sdkDisabled
+            && tracesExporter /= Just "none"
+            && (isJust tracesExporter || hasOtelExporterVar)
   where
-    initializationTimeout = secondsToNominalDiffTime 1
+    isOtelExporterVar (k, v) = "OTEL_EXPORTER_" `isPrefixOf` k && not (null v)
 
 globalTracer :: (MonadIO m) => m Tracer
 globalTracer =
@@ -76,7 +109,6 @@
 inSpan :: (MonadUnliftIO m) => Text -> m a -> m a
 inSpan spanName =
     inSpanWith spanName defaultSpanArguments
-
 withTracer :: (MonadUnliftIO m) => (TracerProvider -> m a) -> m a
 withTracer =
     bracket (liftIO initializeGlobalTracerProvider) shutdown
