How to configure a Logstash JDBC input

A Logstash JDBC input turns rows from a relational database into pipeline events without staging CSV exports or writing a separate poller. Regular polling keeps operational or application data moving into downstream pipelines while the database remains the source of truth.

The jdbc input plugin loads a vendor JDBC driver jar, connects through a JDBC URL, runs a SQL statement, and converts each returned row into a Logstash event. Incremental polling uses sql_last_value, which is written to a metadata file so later runs resume from the previous checkpoint instead of starting from the beginning.

Current Logstash releases do not bundle JDBC drivers, and a custom last_run_metadata_path only works when its parent directory already exists and is writable by the logstash service user. On package-based Linux installs, keeping the driver jar, password file, and checkpoint file in predictable paths makes pipeline validation and service restarts much easier.

Steps to configure a JDBC input in Logstash:

  1. Copy the JDBC driver jar into a dedicated Logstash driver directory.
    $ sudo install -d -m 0755 /usr/share/logstash/vendor/jdbc
    $ sudo install -m 0644 postgresql.jar /usr/share/logstash/vendor/jdbc/postgresql.jar

    The JDBC input does not ship with database drivers. Replace the jar name, jdbc_driver_library, jdbc_driver_class, and jdbc_connection_string with the values for MySQL, MariaDB, SQL Server, Oracle, or another supported JDBC source when needed.

  2. Create directories for the password file and the JDBC checkpoint, then save the database password in a file readable by the logstash group.
    $ sudo install -o root -g logstash -m 0750 -d /etc/logstash/secrets /var/lib/logstash/jdbc_last_run
    $ printf '%s\n' 'db-reader-password' | sudo tee /etc/logstash/secrets/jdbc-orders.pwd >/dev/null
    $ sudo chown root:logstash /etc/logstash/secrets/jdbc-orders.pwd
    $ sudo chmod 0640 /etc/logstash/secrets/jdbc-orders.pwd

    If last_run_metadata_path points to a custom file, its parent directory must exist before the pipeline starts or the JDBC scheduler can query successfully but fail when it tries to write the checkpoint.

    A password file keeps the secret out of the pipeline definition. Use the Logstash keystore instead when the service already relies on keystore-backed substitutions.

  3. Create the pipeline configuration file at /etc/logstash/conf.d/30-jdbc-orders.conf.
    input {
      jdbc {
        id => "jdbc_orders"
        jdbc_driver_library => "/usr/share/logstash/vendor/jdbc/postgresql.jar"
        jdbc_driver_class => "org.postgresql.Driver"
        jdbc_connection_string => "jdbc:postgresql://db.example.net:5432/app"
        jdbc_user => "logstash"
        jdbc_password_filepath => "/etc/logstash/secrets/jdbc-orders.pwd"
        period => "1m"
        statement => "SELECT id, customer_email, updated_at FROM orders WHERE updated_at > :sql_last_value ORDER BY updated_at ASC, id ASC"
        use_column_value => true
        tracking_column => "updated_at"
        tracking_column_type => "timestamp"
        jdbc_validate_connection => true
        jdbc_validation_timeout => 50
        last_run_metadata_path => "/var/lib/logstash/jdbc_last_run/orders.yml"
      }
    }
    
    output {
      stdout {
        codec => json_lines
      }
    }

    Use schedule with a cron expression such as */5 * * * * UTC when the poll must align to wall-clock times. Keep period when only a fixed interval matters, or omit both lines to run the statement once at startup.

    The stdout output is useful for the first successful poll because it proves the JDBC input is emitting events before another destination is added. Replace it with the target output after validation. Related: How to configure Logstash output to Elasticsearch

  4. Test the pipeline configuration with the packaged settings directory and a temporary data path.
    $ sudo -u logstash /usr/share/logstash/bin/logstash --path.settings /etc/logstash --path.data /tmp/logstash-configtest --config.test_and_exit
    Using bundled JDK: /usr/share/logstash/jdk
    [2026-04-08T00:00:04,469][INFO ][logstash.runner          ] Starting Logstash {"logstash.version"=>"9.3.1"}
    ##### snipped #####
    Configuration OK
    [2026-04-08T00:00:08,208][INFO ][logstash.runner          ] Using config.test_and_exit mode. Config Validation Result: OK. Exiting Logstash

    The temporary --path.data directory keeps the syntax check away from the running service state in /var/lib/logstash.

  5. Restart the Logstash service to load the JDBC pipeline.
    $ sudo systemctl restart logstash
  6. Confirm the JDBC input is polling and that the configured plugin ID appears in pipeline stats.
    $ curl -s http://localhost:9600/_node/stats/pipelines/main?pretty=true
    {
      "pipelines" : {
        "main" : {
          "events" : {
            "in" : 3,
            "filtered" : 3,
            "out" : 3
          },
          "plugins" : {
            "inputs" : [ {
              "id" : "jdbc_orders",
              "name" : "jdbc",
              "events" : {
                "out" : 3
              }
            } ]
          }
        }
      }
    }

    The monitoring API binds to 127.0.0.1 by default on package installs. If the API is secured or moved to another address or port, query the configured endpoint instead.

  7. Confirm the checkpoint file was written after the first successful poll.
    $ sudo cat /var/lib/logstash/jdbc_last_run/orders.yml
    --- 2026-04-08 00:14:00.000000000 Z

    If the SQL query is running but the checkpoint file never appears, re-check the parent directory named in last_run_metadata_path and make sure the logstash service user can write to it.