Tags and groups help you organize k6 test results for better analysis. They let you categorize metrics, filter results, and structure your tests logically.
Tags are key-value pairs attached to metrics, checks, and requests. They enable you to:
Filter metrics by request type, endpoint, or custom criteria
Set thresholds for specific tagged metrics
Analyze results by different dimensions
Compare performance across different parts of your test
k6 provides two types of tags:
System tags Automatically added by k6 Examples: status, method, url
User-defined tags Custom tags you add to your test Examples: endpoint, type, priority
k6 automatically tags metrics with system-defined metadata:
Tag Description Example statusHTTP status code 200, 404, 500methodHTTP method GET, POST, PUTurlRequest URL http://test.k6.ionameRequest name http://test.k6.iogroupGroup path ::login::submitscenarioScenario name defaultexpected_responseExpected response flag true, falseprotoProtocol version HTTP/1.1, HTTP/2errorError message Network error string tls_versionTLS version tls1.2, tls1.3
These tags are disabled by default but can be enabled:
Tag Description vuVirtual user ID iterIteration number ipRemote server IP ocsp_statusOCSP status
Enable them using the systemTags option:
export const options = {
systemTags: [ 'status' , 'method' , 'url' , 'vu' , 'iter' ],
};
Add custom tags to requests, checks, and metrics:
Tag HTTP requests
import http from 'k6/http' ;
export default function () {
http . get ( 'https://quickpizza.grafana.com/' , {
tags: {
type: 'homepage' ,
priority: 'critical' ,
},
});
}
Tag checks
import http from 'k6/http' ;
import { check } from 'k6' ;
export default function () {
const res = http . get ( 'https://quickpizza.grafana.com/' );
check (
res ,
{ 'status is 200' : ( r ) => r . status === 200 },
{ check_type: 'critical' }
);
}
Tag custom metrics
import http from 'k6/http' ;
import { Trend } from 'k6/metrics' ;
const apiDuration = new Trend ( 'api_duration' );
export default function () {
const res = http . get ( 'https://quickpizza.grafana.com/api/pizzas' );
apiDuration . add ( res . timings . duration , {
endpoint: '/api/pizzas' ,
version: 'v1' ,
});
}
Set tags that apply to all metrics in your test:
export const options = {
tags: {
environment: 'production' ,
team: 'backend' ,
version: '1.2.3' ,
},
};
k6 run --tag environment=staging --tag team=frontend script.js
Test-wide tags are useful for:
Comparing results across test runs
Filtering results in monitoring systems
Organizing tests by environment or team
Thresholds on tagged metrics
Set different thresholds for different tagged requests:
import http from 'k6/http' ;
import { sleep } from 'k6' ;
export const options = {
thresholds: {
'http_req_duration{type:API}' : [ 'p(95)<500' ],
'http_req_duration{type:static}' : [ 'p(95)<200' ],
},
};
export default function () {
// API requests
http . get ( 'https://quickpizza.grafana.com/api/pizzas' , {
tags: { type: 'API' },
});
// Static content
http . get ( 'https://quickpizza.grafana.com/favicon.ico' , {
tags: { type: 'static' },
});
sleep ( 1 );
}
This allows different performance requirements for different request types.
Groups
Groups organize test code into logical sections and automatically tag all operations within them.
Basic groups
import http from 'k6/http' ;
import { group , sleep } from 'k6' ;
export default function () {
group ( 'visit product listing' , function () {
http . get ( 'https://example.com/products' );
});
group ( 'add to cart' , function () {
http . post ( 'https://example.com/cart' , {});
});
group ( 'checkout' , function () {
http . post ( 'https://example.com/checkout' , {});
});
sleep ( 1 );
}
All requests within a group are automatically tagged with group tag.
Nested groups
Groups can be nested for hierarchical organization:
import http from 'k6/http' ;
import { group } from 'k6' ;
export default function () {
group ( 'user workflow' , function () {
group ( 'login' , function () {
http . get ( 'https://example.com/login' );
http . post ( 'https://example.com/auth' , {});
});
group ( 'browse' , function () {
http . get ( 'https://example.com/products' );
});
});
}
The group tag shows the full path: ::user workflow::login
Group duration metric
k6 automatically creates a group_duration metric for each group:
import http from 'k6/http' ;
import { group } from 'k6' ;
export const options = {
thresholds: {
'group_duration{group:::login}' : [ 'avg<1000' ],
'group_duration{group:::checkout}' : [ 'avg<2000' ],
},
};
export default function () {
group ( 'login' , function () {
http . get ( 'https://example.com/login' );
http . post ( 'https://example.com/auth' , {});
});
group ( 'checkout' , function () {
http . post ( 'https://example.com/checkout' , {});
});
}
Set tags dynamically during test execution:
import http from 'k6/http' ;
import exec from 'k6/execution' ;
import { group } from 'k6' ;
export const options = {
thresholds: {
'http_reqs{container_group:main}' : [ 'count==3' ],
},
};
export default function () {
// Set dynamic tag
exec . vu . tags . containerGroup = 'main' ;
group ( 'main' , function () {
http . get ( 'https://test.k6.io' );
group ( 'sub' , function () {
http . get ( 'https://quickpizza.grafana.com/' );
});
http . get ( 'https://quickpizza.grafana.com/api/headers' );
});
// Remove tag
delete exec . vu . tags . containerGroup ;
http . get ( 'https://quickpizza.grafana.com/api/delay/3' );
}
Tagging stages
Use helper functions to tag operations by their test stage:
import http from 'k6/http' ;
import { tagWithCurrentStageIndex } from 'https://jslib.k6.io/k6-utils/1.3.0/index.js' ;
export const options = {
stages: [
{ target: 5 , duration: '2s' },
{ target: 10 , duration: '5s' },
],
};
export default function () {
tagWithCurrentStageIndex ();
// All requests tagged with current stage index
http . get ( 'https://test.k6.io' ); // {stage: "0"} or {stage: "1"}
}
Complete example
Here’s a comprehensive example using tags and groups:
import http from 'k6/http' ;
import { check , group , sleep } from 'k6' ;
import { Trend } from 'k6/metrics' ;
const apiDuration = new Trend ( 'api_duration' );
export const options = {
vus: 10 ,
duration: '30s' ,
// Test-wide tags
tags: {
environment: 'production' ,
test_type: 'load' ,
},
// Thresholds using tags
thresholds: {
'http_req_duration{type:api}' : [ 'p(95)<500' ],
'http_req_duration{type:static}' : [ 'p(95)<100' ],
'group_duration{group:::API Calls}' : [ 'avg<400' ],
'checks{critical:true}' : [ 'rate>0.99' ],
},
};
const baseUrl = 'https://quickpizza.grafana.com' ;
export default function () {
group ( 'Homepage' , function () {
const res = http . get ( baseUrl , {
tags: { type: 'static' , page: 'home' },
});
check (
res ,
{ 'status is 200' : ( r ) => r . status === 200 },
{ critical: 'true' }
);
});
group ( 'API Calls' , function () {
const res = http . get ( ` ${ baseUrl } /api/pizzas` , {
tags: { type: 'api' , endpoint: '/pizzas' },
});
apiDuration . add ( res . timings . duration , {
endpoint: '/pizzas' ,
});
check (
res ,
{ 'api status is 200' : ( r ) => r . status === 200 },
{ critical: 'true' }
);
});
sleep ( 1 );
}
Best practices
Use descriptive tag names Make tag names clear and consistent across your tests.
Tag by request type Separate API, static content, and other request types with tags.
Group logical workflows Use groups to organize user journeys and workflows.
Set thresholds per tag Define different performance requirements for different request types.
Avoid one group per request. Wrapping every request in a group adds unnecessary complexity. Use groups for logical sections, not individual requests.