Wage trends in Germany from 2015 to 2025

Model: Kimi K2.6 Checked on:

Based on Eurostat’s Labour Cost Index data for Germany, nominal wages and salaries (excluding apprentices) in the broad economy (NACE B‑S: industry, construction and services) show a clear upward trend from 2015 to 2025, with a marked acceleration after 2021.

Key trend figures (annual index, 2020 = 100)12

Year Index Annual growth
2015 87.5 +2.3 %
2016 89.3 +2.2 %
2017 92.3 +3.3 %
2018 94.9 +2.9 %
2019 97.8 +3.0 %
2020 100.0 +2.2 %
2021 101.2 +1.2 %
2022 105.5 +4.2 %
2023 110.0 +4.3 %
2024 115.7 +5.1 %
2025 119.9 +3.7 %

Summary of the trend

The quarterly, seasonally and calendar‑adjusted series confirms this pattern: a visible step‑up in year‑over‑year growth starting in 2022 and continuing through the latest available quarters31.

Visualisations of the index and growth rates are provided in the generated charts: the quarterly index trajectory 1, the annual growth‑rate bars 1, and the quarterly year‑over‑year growth profile 1.

  1. Dataset: lc_lci_r2_q Dataset: lc_lci_r2_a

    Images

    germany_wage_index_quarterly.svg
    germany_wage_growth_annual.svg
    germany_wage_growth_quarterly.svg

    Dataset filters for lc_lci_r2_q

    FilterValue(s)
    geoDE
    nace_r2B-S
    lcstructD11
    unitI20, PCH_SM
    s_adjSCA
    sinceTimePeriod2015

    Dataset filters for lc_lci_r2_a

    FilterValue(s)
    geoDE
    nace_r2B-S
    lcstructD11
    unitI20, PCH_PRE
    sinceTimePeriod2015

    Program output

    Annual Index values:
     year  value
     2015   87.5
     2016   89.3
     2017   92.3
     2018   94.9
     2019   97.8
     2020  100.0
     2021  101.2
     2022  105.5
     2023  110.0
     2024  115.7
     2025  119.9
    
    Annual Growth rates:
     year  value
     2015    2.3
     2016    2.2
     2017    3.3
     2018    2.9
     2019    3.0
     2020    2.2
     2021    1.2
     2022    4.2
     2023    4.3
     2024    5.1
     2025    3.7
    
    Plots saved successfully.
    

    Code

    import pandas as pd
    import matplotlib.pyplot as plt
    
    # Load quarterly data
    df_q = pd.read_csv(DATASET_FILE0, low_memory=False)
    # Filter for B-S, D11, I20, SCA
    df_q_idx = df_q[(df_q['nace_r2'] == 'B-S') & (df_q['lcstruct'] == 'D11') & (df_q['unit'] == 'I20') & (df_q['s_adj'] == 'SCA')].copy()
    df_q_idx['period'] = pd.PeriodIndex(df_q_idx['time'], freq='Q')
    
    df_q_pch = df_q[(df_q['nace_r2'] == 'B-S') & (df_q['lcstruct'] == 'D11') & (df_q['unit'] == 'PCH_SM') & (df_q['s_adj'] == 'SCA')].copy()
    df_q_pch['period'] = pd.PeriodIndex(df_q_pch['time'], freq='Q')
    
    # Load annual data
    df_a = pd.read_csv(DATASET_FILE1, low_memory=False)
    df_a_idx = df_a[(df_a['nace_r2'] == 'B-S') & (df_a['lcstruct'] == 'D11') & (df_a['unit'] == 'I20')].copy()
    df_a_idx['year'] = df_a_idx['time'].astype(int)
    
    df_a_pch = df_a[(df_a['nace_r2'] == 'B-S') & (df_a['lcstruct'] == 'D11') & (df_a['unit'] == 'PCH_PRE')].copy()
    df_a_pch['year'] = df_a_pch['time'].astype(int)
    
    print("Annual Index values:")
    print(df_a_idx.sort_values('year')[['year','value']].to_string(index=False))
    print("\nAnnual Growth rates:")
    print(df_a_pch.sort_values('year')[['year','value']].to_string(index=False))
    
    # Plot 1: Quarterly Index
    fig, ax = plt.subplots(figsize=(10,6))
    df_q_idx_sorted = df_q_idx.sort_values('period')
    # Convert period to timestamp for plotting
    x = df_q_idx_sorted['period'].dt.to_timestamp()
    ax.plot(x, df_q_idx_sorted['value'], marker='o', linestyle='-', markersize=3, color='navy')
    ax.set_title('Germany: Nominal Wages & Salaries Index (2020=100)\nNACE B-S, Seasonally and Calendar Adjusted')
    ax.set_xlabel('Quarter')
    ax.set_ylabel('Index (2020=100)')
    ax.grid(True, alpha=0.3)
    fig.autofmt_xdate()
    fig.tight_layout()
    fig.savefig('germany_wage_index_quarterly.svg', format='svg')
    plt.close()
    
    # Plot 2: Annual Growth Rate
    fig, ax = plt.subplots(figsize=(10,6))
    df_a_pch_sorted = df_a_pch.sort_values('year')
    ax.bar(df_a_pch_sorted['year'], df_a_pch_sorted['value'], color='steelblue', edgecolor='black', linewidth=0.5)
    ax.set_title('Germany: Annual Growth Rate of Nominal Wages & Salaries (%)\nNACE B-S, Year-over-Year Change')
    ax.set_xlabel('Year')
    ax.set_ylabel('Percentage Change (%)')
    ax.axhline(0, color='red', linewidth=0.5)
    ax.grid(True, alpha=0.3, axis='y')
    fig.tight_layout()
    fig.savefig('germany_wage_growth_annual.svg', format='svg')
    plt.close()
    
    # Plot 3: Quarterly year-over-year growth
    fig, ax = plt.subplots(figsize=(10,6))
    df_q_pch_sorted = df_q_pch.sort_values('period')
    x = df_q_pch_sorted['period'].dt.to_timestamp()
    ax.plot(x, df_q_pch_sorted['value'], marker='o', linestyle='-', markersize=3, color='green')
    ax.set_title('Germany: Quarterly Year-over-Year Growth of Nominal Wages & Salaries (%)\nNACE B-S, Seasonally and Calendar Adjusted')
    ax.set_xlabel('Quarter')
    ax.set_ylabel('Percentage Change (%)')
    ax.axhline(0, color='red', linewidth=0.5)
    ax.grid(True, alpha=0.3)
    fig.autofmt_xdate()
    fig.tight_layout()
    fig.savefig('germany_wage_growth_quarterly.svg', format='svg')
    plt.close()
    
    print("\nPlots saved successfully.")
    
  2. Eurostat dataset: Labour cost index by NACE Rev. 2 activity - nominal value, annual data
  3. Eurostat dataset: Labour cost index by NACE Rev. 2 activity - nominal value, quarterly data