diff --git a/lib/install/tailwindcss.rb b/lib/install/tailwindcss.rb
index 413f86b8..40f18746 100644
--- a/lib/install/tailwindcss.rb
+++ b/lib/install/tailwindcss.rb
@@ -1,5 +1,6 @@
 APPLICATION_LAYOUT_PATH             = Rails.root.join("app/views/layouts/application.html.erb")
 CENTERING_CONTAINER_INSERTION_POINT = /^\s*<%= yield %>/.freeze
+DEVELOPMENT_ENVIRONMENT_CONFIG_PATH = Rails.root.join("config/environments/development.rb")
 
 if APPLICATION_LAYOUT_PATH.exist?
   say "Add Tailwindcss include tags and container element in application layout"
@@ -16,6 +17,15 @@
   say %(        Add <%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %> within the <head> tag in your custom layout.)
 end
 
+if DEVELOPMENT_ENVIRONMENT_CONFIG_PATH.exist?
+  say "Enable Tailwindcss server process in development"
+  insert_into_file DEVELOPMENT_ENVIRONMENT_CONFIG_PATH.to_s, <<~ERB.indent(2), before: /^end$/
+
+    # Automatically watch and build Tailwindcss when 'rails server' is started.
+    config.tailwindcss.server_process = true
+  ERB
+end
+
 say "Build into app/assets/builds"
 empty_directory "app/assets/builds"
 keep_file "app/assets/builds"
diff --git a/lib/tailwindcss-rails.rb b/lib/tailwindcss-rails.rb
index 112809c5..867663c6 100644
--- a/lib/tailwindcss-rails.rb
+++ b/lib/tailwindcss-rails.rb
@@ -5,3 +5,4 @@ module Tailwindcss
 require_relative "tailwindcss/version"
 require_relative "tailwindcss/engine"
 require_relative "tailwindcss/commands"
+require_relative "tailwindcss/server_process"
diff --git a/lib/tailwindcss/engine.rb b/lib/tailwindcss/engine.rb
index 4b9b9fdc..fb60f1e3 100644
--- a/lib/tailwindcss/engine.rb
+++ b/lib/tailwindcss/engine.rb
@@ -2,6 +2,9 @@
 
 module Tailwindcss
   class Engine < ::Rails::Engine
+    config.tailwindcss = ActiveSupport::OrderedOptions.new
+    config.tailwindcss.server_process = false # Rails.env.development?
+
     initializer "tailwindcss.assets" do
       Rails.application.config.assets.precompile += %w( inter-font.css )
     end
@@ -13,5 +16,9 @@ class Engine < ::Rails::Engine
     config.app_generators do |g|
       g.template_engine :tailwindcss
     end
+
+    server do
+      ServerProcess.start if config.tailwindcss.server_process
+    end
   end
 end
diff --git a/lib/tailwindcss/server_process.rb b/lib/tailwindcss/server_process.rb
new file mode 100644
index 00000000..fbca382c
--- /dev/null
+++ b/lib/tailwindcss/server_process.rb
@@ -0,0 +1,149 @@
+module Tailwindcss
+  class ServerProcess
+    attr_reader :server, :pid
+
+    def self.start
+      new.start
+    end
+
+    def initialize
+      @server = Server.new(self)
+    end
+
+    def start
+      @pid = existing_process || start_process
+      server.monitor_process
+      server.exit_hook
+    end
+
+    def stop
+      return if dead?
+
+      Process.kill(:INT, pid)
+      Process.wait(pid)
+    rescue Errno::ECHILD, Errno::ESRCH
+    end
+
+    def dead?
+      Process.wait(pid, Process::WNOHANG)
+      false
+    rescue Errno::ECHILD, Errno::ESRCH
+      true
+    end
+
+    private
+
+    def existing_process
+      if (pid = Pidfile.pid)
+        begin
+          Process.kill 0, pid
+          pid
+        rescue Errno::ESRCH
+          # Process does not exist
+        rescue Errno::EPERM
+          # Ignore process owned by another user
+        end
+      end
+    end
+
+    def start_process
+      pid = fork do
+        Pidfile.write
+        monitor_server
+        exit_hook
+        # Using IO.popen(command, 'r+') will avoid watch_command read from $stdin.
+        # If we use system(*command) instead, IRB and Debug can't read from $stdin
+        # correctly bacause some keystrokes will be taken by watch_command.
+        IO.popen(Commands.watch_command, 'r+') do |io|
+          IO.copy_stream(io, $stdout)
+        end
+      ensure
+        Pidfile.delete
+      end
+      Process.detach pid
+      pid
+    end
+
+    def monitor_server
+      Thread.new do
+        loop do
+          if server.dead?
+            puts "Tailwind detected server has gone away"
+            exit
+          end
+          sleep 2
+        end
+      end
+    end
+
+    def exit_hook
+      at_exit do
+        puts "Stopping tailwind..."
+        server.stop
+      end
+    end
+
+    module Pidfile
+      def self.path
+        Rails.root.join("tmp", "pids", "tailwindcss.txt")
+      end
+
+      def self.read
+        File.read(path, mode: "rb:UTF-8")
+      rescue Errno::ENOENT
+        # File does not exist
+      end
+
+      def self.write
+        File.write(path, Process.pid, mode: "wb:UTF-8")
+      end
+
+      def self.delete
+        File.exist?(path) && File.delete(path)
+      end
+
+      def self.pid
+        Integer(read)
+      rescue ArgumentError, TypeError
+        # Invalid content
+        delete
+      end
+    end
+
+    class Server
+      attr_reader :process, :pid
+
+      def initialize(process)
+        @process = process
+        @pid = Process.pid
+      end
+
+      def monitor_process
+        Thread.new do
+          loop do
+            if process.dead?
+              puts "Detected tailwind has gone away, stopping server..."
+              exit
+            end
+            sleep 2
+          end
+        end
+      end
+
+      def exit_hook
+        at_exit do
+          process.stop
+        end
+      end
+
+      def dead?
+        Process.ppid != pid
+      end
+
+      def stop
+        Process.kill(:INT, pid)
+      rescue Errno::ECHILD, Errno::ESRCH
+      end
+    end
+  end
+end